Merge "Benchmark: Add support to configure encoders through test parameters"
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/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/g711/Android.bp b/media/codec2/components/g711/Android.bp
index 3ede68c..0101b1a 100644
--- a/media/codec2/components/g711/Android.bp
+++ b/media/codec2/components/g711/Android.bp
@@ -7,6 +7,8 @@
srcs: ["C2SoftG711Dec.cpp"],
+ static_libs: ["codecs_g711dec"],
+
cflags: [
"-DALAW",
],
@@ -20,4 +22,6 @@
],
srcs: ["C2SoftG711Dec.cpp"],
+
+ static_libs: ["codecs_g711dec"],
}
diff --git a/media/codec2/components/g711/C2SoftG711Dec.cpp b/media/codec2/components/g711/C2SoftG711Dec.cpp
index 4ff0793..7f9c34e 100644
--- a/media/codec2/components/g711/C2SoftG711Dec.cpp
+++ b/media/codec2/components/g711/C2SoftG711Dec.cpp
@@ -22,7 +22,7 @@
#include <C2PlatformSupport.h>
#include <SimpleC2Interface.h>
-
+#include <g711Dec.h>
#include "C2SoftG711Dec.h"
namespace android {
@@ -224,53 +224,6 @@
return C2_OK;
}
-#ifdef ALAW
-void C2SoftG711Dec::DecodeALaw(
- int16_t *out, const uint8_t *in, size_t inSize) {
- while (inSize > 0) {
- inSize--;
- int32_t x = *in++;
-
- int32_t ix = x ^ 0x55;
- ix &= 0x7f;
-
- int32_t iexp = ix >> 4;
- int32_t mant = ix & 0x0f;
-
- if (iexp > 0) {
- mant += 16;
- }
-
- mant = (mant << 4) + 8;
-
- if (iexp > 1) {
- mant = mant << (iexp - 1);
- }
-
- *out++ = (x > 127) ? mant : -mant;
- }
-}
-#else
-void C2SoftG711Dec::DecodeMLaw(
- int16_t *out, const uint8_t *in, size_t inSize) {
- while (inSize > 0) {
- inSize--;
- int32_t x = *in++;
-
- int32_t mantissa = ~x;
- int32_t exponent = (mantissa >> 4) & 7;
- int32_t segment = exponent + 1;
- mantissa &= 0x0f;
-
- int32_t step = 4 << segment;
-
- int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
-
- *out++ = (x < 0x80) ? -abs : abs;
- }
-}
-#endif
-
class C2SoftG711DecFactory : public C2ComponentFactory {
public:
C2SoftG711DecFactory() : mHelper(std::static_pointer_cast<C2ReflectorHelper>(
diff --git a/media/codec2/components/g711/C2SoftG711Dec.h b/media/codec2/components/g711/C2SoftG711Dec.h
index 23e8ffc..f93840b 100644
--- a/media/codec2/components/g711/C2SoftG711Dec.h
+++ b/media/codec2/components/g711/C2SoftG711Dec.h
@@ -45,12 +45,6 @@
std::shared_ptr<IntfImpl> mIntf;
bool mSignalledOutputEos;
-#ifdef ALAW
- void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
-#else
- void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
-#endif
-
C2_DO_NOT_COPY(C2SoftG711Dec);
};
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 6db4387..e4b911d 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -497,7 +497,7 @@
status_t C2SoftHevcDec::initDecoder() {
if (OK != createDecoder()) return UNKNOWN_ERROR;
mNumCores = MIN(getCpuCoreCount(), MAX_NUM_CORES);
- mStride = ALIGN128(mWidth);
+ mStride = ALIGN32(mWidth);
mSignalledError = false;
resetPlugin();
(void) setNumCores();
@@ -515,10 +515,20 @@
size_t inSize,
uint32_t tsMarker) {
uint32_t displayStride = mStride;
+ if (outBuffer) {
+ C2PlanarLayout layout;
+ layout = outBuffer->layout();
+ displayStride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc;
+ }
uint32_t displayHeight = mHeight;
size_t lumaSize = displayStride * displayHeight;
size_t chromaSize = lumaSize >> 2;
+ if (mStride != displayStride) {
+ mStride = displayStride;
+ if (OK != setParams(mStride, IVD_DECODE_FRAME)) return false;
+ }
+
ps_decode_ip->u4_size = sizeof(ivd_video_decode_ip_t);
ps_decode_ip->e_cmd = IVD_CMD_VIDEO_DECODE;
if (inBuffer) {
@@ -752,24 +762,21 @@
ALOGE("not supposed to be here, invalid decoder context");
return C2_CORRUPTED;
}
- if (mStride != ALIGN128(mWidth)) {
- mStride = ALIGN128(mWidth);
- if (OK != setParams(mStride, IVD_DECODE_FRAME)) return C2_CORRUPTED;
- }
if (mOutBlock &&
- (mOutBlock->width() != mStride || mOutBlock->height() != mHeight)) {
+ (mOutBlock->width() != ALIGN32(mWidth) || mOutBlock->height() != mHeight)) {
mOutBlock.reset();
}
if (!mOutBlock) {
uint32_t format = HAL_PIXEL_FORMAT_YV12;
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
- c2_status_t err = pool->fetchGraphicBlock(mStride, mHeight, format, usage, &mOutBlock);
+ c2_status_t err =
+ pool->fetchGraphicBlock(ALIGN32(mWidth), mHeight, format, usage, &mOutBlock);
if (err != C2_OK) {
ALOGE("fetchGraphicBlock for Output failed with status %d", err);
return err;
}
ALOGV("provided (%dx%d) required (%dx%d)",
- mOutBlock->width(), mOutBlock->height(), mStride, mHeight);
+ mOutBlock->width(), mOutBlock->height(), ALIGN32(mWidth), mHeight);
}
return C2_OK;
@@ -904,7 +911,7 @@
if (0 < s_decode_op.u4_pic_wd && 0 < s_decode_op.u4_pic_ht) {
if (mHeaderDecoded == false) {
mHeaderDecoded = true;
- setParams(ALIGN128(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
+ setParams(ALIGN32(s_decode_op.u4_pic_wd), IVD_DECODE_FRAME);
}
if (s_decode_op.u4_pic_wd != mWidth || s_decode_op.u4_pic_ht != mHeight) {
mWidth = s_decode_op.u4_pic_wd;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.h b/media/codec2/components/hevc/C2SoftHevcDec.h
index aecd101..600d7c1 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.h
+++ b/media/codec2/components/hevc/C2SoftHevcDec.h
@@ -37,8 +37,7 @@
#define ivdext_ctl_set_num_cores_op_t ihevcd_cxa_ctl_set_num_cores_op_t
#define ivdext_ctl_get_vui_params_ip_t ihevcd_cxa_ctl_get_vui_params_ip_t
#define ivdext_ctl_get_vui_params_op_t ihevcd_cxa_ctl_get_vui_params_op_t
-#define ALIGN64(x) ((((x) + 63) >> 6) << 6)
-#define ALIGN128(x) ((((x) + 127) >> 7) << 7)
+#define ALIGN32(x) ((((x) + 31) >> 5) << 5)
#define MAX_NUM_CORES 4
#define IVDEXT_CMD_CTL_SET_NUM_CORES \
(IVD_CONTROL_API_COMMAND_TYPE_T)IHEVCD_CXA_CMD_CTL_SET_NUM_CORES
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index 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/codecs/g711/decoder/Android.bp b/media/codecs/g711/decoder/Android.bp
new file mode 100644
index 0000000..377833f
--- /dev/null
+++ b/media/codecs/g711/decoder/Android.bp
@@ -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.
+ */
+
+cc_library_static {
+ name: "codecs_g711dec",
+ vendor_available: true,
+ host_supported: true,
+
+ srcs: [
+ "g711DecAlaw.cpp",
+ "g711DecMlaw.cpp",
+ ],
+
+ export_include_dirs: ["."],
+
+ cflags: ["-Werror"],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ },
+ apex_available: ["com.android.media.swcodec"],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
+}
diff --git a/media/codecs/g711/decoder/g711Dec.h b/media/codecs/g711/decoder/g711Dec.h
new file mode 100644
index 0000000..ca357a5
--- /dev/null
+++ b/media/codecs/g711/decoder/g711Dec.h
@@ -0,0 +1,41 @@
+/*
+ * 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 G711_DEC_H_
+#define G711_DEC_H_
+
+/**
+ * @file g711Dec.h
+ * @brief g711 Decoder API: DecodeALaw and DecodeMLaw
+ */
+
+/** Decodes input bytes of size inSize according to ALAW
+ *
+ * @param [in] out <tt>int16_t*</tt>: output buffer to be filled with decoded bytes.
+ * @param [in] in <tt>const uint8_t*</tt>: input buffer containing bytes to be decoded.
+ * @param [in] inSize <tt>size_t</tt>: size of the input buffer.
+ */
+void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+/** Decodes input bytes of size inSize according to MLAW
+ *
+ * @param [in] out <tt>int16_t*</tt>: output buffer to be filled with decoded bytes.
+ * @param [in] in <tt>const uint8_t*</tt>: input buffer containing bytes to be decoded.
+ * @param [in] inSize <tt>size_t</tt>: size of the input buffer.
+ */
+void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize);
+
+#endif // G711_DECODER_H_
diff --git a/media/codecs/g711/decoder/g711DecAlaw.cpp b/media/codecs/g711/decoder/g711DecAlaw.cpp
new file mode 100644
index 0000000..e41a7b4
--- /dev/null
+++ b/media/codecs/g711/decoder/g711DecAlaw.cpp
@@ -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.
+ */
+
+#include <stddef.h>
+#include <stdint.h>
+
+void DecodeALaw(int16_t *out, const uint8_t *in, size_t inSize) {
+ if (out != nullptr && in != nullptr) {
+ while (inSize > 0) {
+ inSize--;
+ int32_t x = *in++;
+
+ int32_t ix = x ^ 0x55;
+ ix &= 0x7f;
+
+ int32_t iexp = ix >> 4;
+ int32_t mant = ix & 0x0f;
+
+ if (iexp > 0) {
+ mant += 16;
+ }
+
+ mant = (mant << 4) + 8;
+
+ if (iexp > 1) {
+ mant = mant << (iexp - 1);
+ }
+
+ *out++ = (x > 127) ? mant : -mant;
+ }
+ }
+}
diff --git a/media/codecs/g711/decoder/g711DecMlaw.cpp b/media/codecs/g711/decoder/g711DecMlaw.cpp
new file mode 100644
index 0000000..bb2caea
--- /dev/null
+++ b/media/codecs/g711/decoder/g711DecMlaw.cpp
@@ -0,0 +1,38 @@
+/*
+ * 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 <stddef.h>
+#include <stdint.h>
+
+void DecodeMLaw(int16_t *out, const uint8_t *in, size_t inSize) {
+ if (out != nullptr && in != nullptr) {
+ while (inSize > 0) {
+ inSize--;
+ int32_t x = *in++;
+
+ int32_t mantissa = ~x;
+ int32_t exponent = (mantissa >> 4) & 7;
+ int32_t segment = exponent + 1;
+ mantissa &= 0x0f;
+
+ int32_t step = 4 << segment;
+
+ int32_t abs = (0x80l << exponent) + step * mantissa + step / 2 - 4 * 33;
+
+ *out++ = (x < 0x80) ? -abs : abs;
+ }
+ }
+}
diff --git a/media/codecs/g711/fuzzer/Android.bp b/media/codecs/g711/fuzzer/Android.bp
new file mode 100644
index 0000000..1aee7f5
--- /dev/null
+++ b/media/codecs/g711/fuzzer/Android.bp
@@ -0,0 +1,44 @@
+/******************************************************************************
+ *
+ * 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: "g711alaw_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "g711_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "codecs_g711dec",
+ ],
+ cflags: [
+ "-DALAW",
+ ],
+}
+
+cc_fuzz {
+ name: "g711mlaw_dec_fuzzer",
+ host_supported: true,
+ srcs: [
+ "g711_dec_fuzzer.cpp",
+ ],
+ static_libs: [
+ "codecs_g711dec",
+ ],
+}
diff --git a/media/codecs/g711/fuzzer/README.md b/media/codecs/g711/fuzzer/README.md
new file mode 100644
index 0000000..0c1c36b
--- /dev/null
+++ b/media/codecs/g711/fuzzer/README.md
@@ -0,0 +1,49 @@
+# Fuzzer for libstagefright_g711dec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for G711 is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+G711 supports two types of decoding:
+1. DecodeALaw
+2. DecodeMLaw
+
+These two decoder API's are fuzzed separately using g711alaw_dec_fuzzer and
+g711mlaw_dec_fuzzer respectively.
+
+##### Maximize utilization of input data
+The plugin feeds the entire input data to the codec as expected by decoder API.
+
+## Build
+
+This describes steps to build g711alaw_dec_fuzzer and g711mlaw_dec_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) g711alaw_dec_fuzzer
+ $ mm -j$(nproc) g711mlaw_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some g711 files to that folder
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/g711alaw_dec_fuzzer/g711alaw_dec_fuzzer CORPUS_DIR
+ $ adb shell /data/fuzz/arm64/g711mlaw_dec_fuzzer/g711mlaw_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/g711alaw_dec_fuzzer/g711alaw_dec_fuzzer CORPUS_DIR
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/g711mlaw_dec_fuzzer/g711mlaw_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/codecs/g711/fuzzer/g711_dec_fuzzer.cpp b/media/codecs/g711/fuzzer/g711_dec_fuzzer.cpp
new file mode 100644
index 0000000..adfbcf5
--- /dev/null
+++ b/media/codecs/g711/fuzzer/g711_dec_fuzzer.cpp
@@ -0,0 +1,58 @@
+/******************************************************************************
+ *
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ *****************************************************************************
+ * Originally developed and contributed by Ittiam Systems Pvt. Ltd, Bangalore
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include "g711Dec.h"
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() = default;
+ void decodeFrames(const uint8_t *data, size_t size);
+};
+
+void Codec::decodeFrames(const uint8_t *data, size_t size) {
+ size_t outputBufferSize = sizeof(int16_t) * size;
+ int16_t *out = new int16_t[outputBufferSize];
+ if (!out) {
+ return;
+ }
+#ifdef ALAW
+ DecodeALaw(out, data, size);
+#else
+ DecodeMLaw(out, data, size);
+#endif
+ delete[] out;
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
+ if (size < 1) {
+ return 0;
+ }
+ Codec *codec = new Codec();
+ if (!codec) {
+ return 0;
+ }
+ codec->decodeFrames(data, size);
+ delete codec;
+ return 0;
+}
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/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/AudioRecord.cpp b/media/libaudioclient/AudioRecord.cpp
index 271e186..3d0d622 100644
--- a/media/libaudioclient/AudioRecord.cpp
+++ b/media/libaudioclient/AudioRecord.cpp
@@ -388,6 +388,9 @@
mFramesReadServerOffset -= mFramesRead + framesFlushed;
mFramesRead = 0;
mProxy->clearTimestamp(); // timestamp is invalid until next server push
+ mPreviousTimestamp.clear();
+ mTimestampRetrogradePositionReported = false;
+ mTimestampRetrogradeTimeReported = false;
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
@@ -570,6 +573,39 @@
timestamp->mPosition[i] += mFramesReadServerOffset;
}
}
+
+ bool timestampRetrogradeTimeReported = false;
+ bool timestampRetrogradePositionReported = false;
+ for (int i = 0; i < ExtendedTimestamp::LOCATION_MAX; ++i) {
+ if (timestamp->mTimeNs[i] >= 0 && mPreviousTimestamp.mTimeNs[i] >= 0) {
+ if (timestamp->mTimeNs[i] < mPreviousTimestamp.mTimeNs[i]) {
+ if (!mTimestampRetrogradeTimeReported) {
+ ALOGD("%s: retrograde time adjusting [%d] current:%lld to previous:%lld",
+ __func__, i, (long long)timestamp->mTimeNs[i],
+ (long long)mPreviousTimestamp.mTimeNs[i]);
+ timestampRetrogradeTimeReported = true;
+ }
+ timestamp->mTimeNs[i] = mPreviousTimestamp.mTimeNs[i];
+ }
+ if (timestamp->mPosition[i] < mPreviousTimestamp.mPosition[i]) {
+ if (!mTimestampRetrogradePositionReported) {
+ ALOGD("%s: retrograde position"
+ " adjusting [%d] current:%lld to previous:%lld",
+ __func__, i, (long long)timestamp->mPosition[i],
+ (long long)mPreviousTimestamp.mPosition[i]);
+ timestampRetrogradePositionReported = true;
+ }
+ timestamp->mPosition[i] = mPreviousTimestamp.mPosition[i];
+ }
+ }
+ }
+ mPreviousTimestamp = *timestamp;
+ if (timestampRetrogradeTimeReported) {
+ mTimestampRetrogradeTimeReported = true;
+ }
+ if (timestampRetrogradePositionReported) {
+ mTimestampRetrogradePositionReported = true;
+ }
}
return status;
}
diff --git a/media/libaudioclient/include/media/AudioRecord.h b/media/libaudioclient/include/media/AudioRecord.h
index 574302b..20a7d14 100644
--- a/media/libaudioclient/include/media/AudioRecord.h
+++ b/media/libaudioclient/include/media/AudioRecord.h
@@ -711,6 +711,10 @@
bool mInOverrun; // whether recorder is currently in overrun state
+ ExtendedTimestamp mPreviousTimestamp{}; // used to detect retrograde motion
+ bool mTimestampRetrogradePositionReported = false; // reduce log spam
+ bool mTimestampRetrogradeTimeReported = false; // reduce log spam
+
private:
class DeathNotifier : public IBinder::DeathRecipient {
public:
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/config/Android.bp b/media/libeffects/config/Android.bp
index 8476f82..8493e30 100644
--- a/media/libeffects/config/Android.bp
+++ b/media/libeffects/config/Android.bp
@@ -15,6 +15,7 @@
"libtinyxml2",
"libutils",
"libmedia_helper",
+ "libcutils",
],
header_libs: ["libaudio_system_headers"],
diff --git a/media/libeffects/config/include/media/EffectsConfig.h b/media/libeffects/config/include/media/EffectsConfig.h
index ef10e0d..57d4dd7 100644
--- a/media/libeffects/config/include/media/EffectsConfig.h
+++ b/media/libeffects/config/include/media/EffectsConfig.h
@@ -35,11 +35,6 @@
/** Default path of effect configuration file. Relative to DEFAULT_LOCATIONS. */
constexpr const char* DEFAULT_NAME = "audio_effects.xml";
-/** Default path of effect configuration file.
- * The /vendor partition is the recommended one, the others are deprecated.
- */
-constexpr const char* DEFAULT_LOCATIONS[] = {"/odm/etc", "/vendor/etc", "/system/etc"};
-
/** Directories where the effect libraries will be search for. */
constexpr const char* LD_EFFECT_LIBRARY_PATH[] =
#ifdef __LP64__
diff --git a/media/libeffects/config/src/EffectsConfig.cpp b/media/libeffects/config/src/EffectsConfig.cpp
index 85fbf11..26eaaf8 100644
--- a/media/libeffects/config/src/EffectsConfig.cpp
+++ b/media/libeffects/config/src/EffectsConfig.cpp
@@ -27,6 +27,7 @@
#include <media/EffectsConfig.h>
#include <media/TypeConverter.h>
+#include <system/audio_config.h>
using namespace tinyxml2;
@@ -338,7 +339,7 @@
return parseWithPath(path);
}
- for (const std::string& location : DEFAULT_LOCATIONS) {
+ for (const std::string& location : audio_get_configuration_paths()) {
std::string defaultPath = location + '/' + DEFAULT_NAME;
if (access(defaultPath.c_str(), R_OK) != 0) {
continue;
diff --git a/media/libmedia/Android.bp b/media/libmedia/Android.bp
index 778ee44..b56ea56 100644
--- a/media/libmedia/Android.bp
+++ b/media/libmedia/Android.bp
@@ -282,11 +282,13 @@
"bionic_libc_platform_headers",
"libstagefright_headers",
"media_ndk_headers",
+ "jni_headers",
],
export_header_lib_headers: [
"libstagefright_headers",
"media_ndk_headers",
+ "jni_headers",
],
shared_libs: [
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/libmediahelper/Android.bp b/media/libmediahelper/Android.bp
index 72edeec..ae135af 100644
--- a/media/libmediahelper/Android.bp
+++ b/media/libmediahelper/Android.bp
@@ -2,6 +2,12 @@
name: "libmedia_helper_headers",
vendor_available: true,
export_include_dirs: ["include"],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library {
@@ -26,4 +32,10 @@
"libmedia_helper_headers",
],
clang: true,
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 18dacb8..abb58be 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -2,6 +2,12 @@
name: "libstagefright_headers",
export_include_dirs: ["include"],
vendor_available: true,
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library_static {
diff --git a/media/libstagefright/MediaExtractorFactory.cpp b/media/libstagefright/MediaExtractorFactory.cpp
index 9e85475..8bce917 100644
--- a/media/libstagefright/MediaExtractorFactory.cpp
+++ b/media/libstagefright/MediaExtractorFactory.cpp
@@ -298,6 +298,12 @@
#endif
"/extractors", NULL, *newList);
+ RegisterExtractors("/system_ext/lib"
+#ifdef __LP64__
+ "64"
+#endif
+ "/extractors", NULL, *newList);
+
newList->sort(compareFunc);
gPlugins = newList;
diff --git a/media/libstagefright/codecs/m4v_h263/dec/Android.bp b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
index b8b83d5..1d7c703 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/Android.bp
+++ b/media/libstagefright/codecs/m4v_h263/dec/Android.bp
@@ -47,6 +47,7 @@
"-DOSCL_IMPORT_REF=",
"-Werror",
+ "-DDEC_NOPOSTPROC",
],
version_script: "exports.lds",
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp
index 9c0fcfa..6c336c8 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/pvdec_api.cpp
@@ -830,7 +830,14 @@
OSCL_EXPORT_REF void PVSetPostProcType(VideoDecControls *decCtrl, int mode)
{
VideoDecData *video = (VideoDecData *)decCtrl->videoDecoderData;
+#ifdef PV_POSTPROC_ON
video->postFilterType = mode;
+#else
+ if (mode != 0) {
+ ALOGE("Post processing filters are not supported");
+ }
+ video->postFilterType = 0;
+#endif
}
diff --git a/media/libstagefright/codecs/mp3dec/Android.bp b/media/libstagefright/codecs/mp3dec/Android.bp
index b630524..75b32bd 100644
--- a/media/libstagefright/codecs/mp3dec/Android.bp
+++ b/media/libstagefright/codecs/mp3dec/Android.bp
@@ -2,6 +2,7 @@
name: "libstagefright_mp3dec",
vendor_available: true,
+ host_supported:true,
srcs: [
"src/pvmp3_normalize.cpp",
"src/pvmp3_alias_reduction.cpp",
@@ -72,6 +73,12 @@
"-DOSCL_UNUSED_ARG(x)=(void)(x)",
"-Werror",
],
+
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
//###############################################################################
diff --git a/media/libstagefright/codecs/mp3dec/fuzzer/Android.bp b/media/libstagefright/codecs/mp3dec/fuzzer/Android.bp
new file mode 100644
index 0000000..2f0eda7
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/fuzzer/Android.bp
@@ -0,0 +1,32 @@
+/******************************************************************************
+ *
+ * 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: "mp3_dec_fuzzer",
+ host_supported: true,
+
+ static_libs: [
+ "libstagefright_mp3dec",
+ ],
+
+ srcs: [
+ "mp3_dec_fuzzer.cpp",
+ ],
+}
diff --git a/media/libstagefright/codecs/mp3dec/fuzzer/README.md b/media/libstagefright/codecs/mp3dec/fuzzer/README.md
new file mode 100644
index 0000000..09dd5c3
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/fuzzer/README.md
@@ -0,0 +1,56 @@
+# Fuzzer for libstagefright_mp3dec decoder
+
+## Plugin Design Considerations
+The fuzzer plugin for mp3 decoder is designed based on the understanding of the
+codec and tries to achieve the following:
+
+##### Maximize code coverage
+
+This fuzzer makes use of the following config parameters:
+1. Equalizer type (parameter name: `equalizerType`)
+
+| Parameter| Valid Values| Configured Value|
+|------------- |-------------| ----- |
+| `equalizerType` | 0. `flat ` 1. `bass_boost ` 2. `rock ` 3. `pop ` 4. `jazz ` 5. `classical ` 6. `talk ` 7. `flat_ ` | Bits 0, 1 and 2 of first byte of input stream |
+| `crcEnabled` | 0. `false ` 1. `true `| Bit 0 of second byte of input stream |
+
+##### 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 used by the decoder.
+ * If the decode operation was un-successful, the input is advanced by 1 byte
+ till it reaches a valid frame or end of stream.
+
+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.
+
+## Build
+
+This describes steps to build mp3_dec_fuzzer binary.
+
+### Android
+
+#### Steps to build
+Build the fuzzer
+```
+ $ mm -j$(nproc) mp3_dec_fuzzer
+```
+
+#### Steps to run
+Create a directory CORPUS_DIR and copy some mp3 files to that folder.
+Push this directory to device.
+
+To run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/mp3_dec_fuzzer/mp3_dec_fuzzer CORPUS_DIR
+```
+To run on host
+```
+ $ $ANDROID_HOST_OUT/fuzz/x86_64/mp3_dec_fuzzer/mp3_dec_fuzzer CORPUS_DIR
+```
+
+## References:
+ * http://llvm.org/docs/LibFuzzer.html
+ * https://github.com/google/oss-fuzz
diff --git a/media/libstagefright/codecs/mp3dec/fuzzer/mp3_dec_fuzzer.cpp b/media/libstagefright/codecs/mp3dec/fuzzer/mp3_dec_fuzzer.cpp
new file mode 100644
index 0000000..847c8c4
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/fuzzer/mp3_dec_fuzzer.cpp
@@ -0,0 +1,237 @@
+/******************************************************************************
+ *
+ * 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 <stdlib.h>
+#include <algorithm>
+
+#include <pvmp3decoder_api.h>
+
+constexpr int kMaxFrameSamples = 4608;
+constexpr int kMaxChannels = 2;
+constexpr e_equalization kEqualizerTypes[] = {flat, bass_boost, rock, pop,
+ jazz, classical, talk, flat_};
+
+static bool parseMp3Header(uint32_t header, size_t *frame_size,
+ uint32_t *out_sampling_rate = nullptr, uint32_t *out_channels = nullptr,
+ uint32_t *out_bitrate = nullptr, uint32_t *out_num_samples = nullptr) {
+ *frame_size = 0;
+ if (out_sampling_rate) *out_sampling_rate = 0;
+ if (out_channels) *out_channels = 0;
+ if (out_bitrate) *out_bitrate = 0;
+ if (out_num_samples) *out_num_samples = 0;
+
+ if ((header & 0xffe00000) != 0xffe00000) {
+ return false;
+ }
+ unsigned version = (header >> 19) & 3;
+ if (version == 0x01) {
+ return false;
+ }
+ unsigned layer = (header >> 17) & 3;
+ if (layer == 0x00) {
+ return false;
+ }
+ unsigned bitrate_index = (header >> 12) & 0x0f;
+ if (bitrate_index == 0 || bitrate_index == 0x0f) {
+ return false;
+ }
+ unsigned sampling_rate_index = (header >> 10) & 3;
+ if (sampling_rate_index == 3) {
+ return false;
+ }
+ static const int kSamplingRateV1[] = {44100, 48000, 32000};
+ int sampling_rate = kSamplingRateV1[sampling_rate_index];
+ if (version == 2 /* V2 */) {
+ sampling_rate /= 2;
+ } else if (version == 0 /* V2.5 */) {
+ sampling_rate /= 4;
+ }
+
+ unsigned padding = (header >> 9) & 1;
+
+ if (layer == 3) { // layer I
+ static const int kBitrateV1[] = {32, 64, 96, 128, 160, 192, 224,
+ 256, 288, 320, 352, 384, 416, 448};
+ static const int kBitrateV2[] = {32, 48, 56, 64, 80, 96, 112,
+ 128, 144, 160, 176, 192, 224, 256};
+
+ int bitrate =
+ (version == 3 /* V1 */) ? kBitrateV1[bitrate_index - 1] : kBitrateV2[bitrate_index - 1];
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+ *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+ if (out_num_samples) {
+ *out_num_samples = 384;
+ }
+ } else { // layer II or III
+ static const int kBitrateV1L2[] = {32, 48, 56, 64, 80, 96, 112,
+ 128, 160, 192, 224, 256, 320, 384};
+ static const int kBitrateV1L3[] = {32, 40, 48, 56, 64, 80, 96,
+ 112, 128, 160, 192, 224, 256, 320};
+ static const int kBitrateV2[] = {8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160};
+ int bitrate;
+ if (version == 3 /* V1 */) {
+ bitrate =
+ (layer == 2 /* L2 */) ? kBitrateV1L2[bitrate_index - 1] : kBitrateV1L3[bitrate_index - 1];
+
+ if (out_num_samples) {
+ *out_num_samples = 1152;
+ }
+ } else { // V2 (or 2.5)
+ bitrate = kBitrateV2[bitrate_index - 1];
+ if (out_num_samples) {
+ *out_num_samples = (layer == 1 /* L3 */) ? 576 : 1152;
+ }
+ }
+
+ if (out_bitrate) {
+ *out_bitrate = bitrate;
+ }
+
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else { // V2 or V2.5
+ size_t tmp = (layer == 1 /* L3 */) ? 72000 : 144000;
+ *frame_size = tmp * bitrate / sampling_rate + padding;
+ }
+ }
+
+ if (out_sampling_rate) {
+ *out_sampling_rate = sampling_rate;
+ }
+
+ if (out_channels) {
+ int channel_mode = (header >> 6) & 3;
+ *out_channels = (channel_mode == 3) ? 1 : 2;
+ }
+
+ return true;
+}
+
+static uint32_t U32_AT(const uint8_t *ptr) {
+ return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+static bool checkHeader(uint8 *header, size_t inSize) {
+ size_t frameSize;
+ size_t totalInSize = 0;
+ bool isValidBuffer = false;
+
+ while (totalInSize + 4 < inSize) {
+ isValidBuffer = true;
+ uint32_t val = U32_AT(header + totalInSize);
+ if (!parseMp3Header(val, &frameSize, nullptr, nullptr, nullptr, nullptr)) {
+ return false;
+ }
+ totalInSize += frameSize;
+ }
+
+ return (isValidBuffer);
+}
+
+class Codec {
+ public:
+ Codec() = default;
+ ~Codec() { deInitDecoder(); }
+
+ bool initDecoder();
+ void decodeFrames(uint8_t *data, size_t size);
+ void deInitDecoder();
+
+ private:
+ tPVMP3DecoderExternal *mConfig = nullptr;
+ void *mDecoderBuf = nullptr;
+};
+
+bool Codec::initDecoder() {
+ mConfig = new tPVMP3DecoderExternal{};
+ if (!mConfig) {
+ return false;
+ }
+ size_t decoderBufSize = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(decoderBufSize);
+ if (!mDecoderBuf) {
+ return false;
+ }
+ memset(mDecoderBuf, 0x0, decoderBufSize);
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ return true;
+}
+
+void Codec::decodeFrames(uint8_t *data, size_t size) {
+ uint8_t equalizerTypeValue = (data[0] & 0x7);
+ mConfig->equalizerType = kEqualizerTypes[equalizerTypeValue];
+ mConfig->crcEnabled = data[1] & 0x1;
+
+ while (size > 0) {
+ bool status = checkHeader(data, size);
+ if (!status) {
+ size--;
+ data++;
+ continue;
+ }
+ size_t outBufSize = kMaxFrameSamples * kMaxChannels;
+ size_t usedBytes = 0;
+ int16_t outputBuf[outBufSize];
+ mConfig->inputBufferCurrentLength = size;
+ mConfig->inputBufferUsedLength = 0;
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->pInputBuffer = data;
+ mConfig->pOutputBuffer = outputBuf;
+ mConfig->outputFrameSize = outBufSize / sizeof(int16_t);
+
+ ERROR_CODE decoderErr;
+ decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf);
+ if (decoderErr != NO_DECODING_ERROR) {
+ size--;
+ data++;
+ } else {
+ usedBytes = std::min((int32_t)size, mConfig->inputBufferUsedLength);
+ size -= usedBytes;
+ data += usedBytes;
+ }
+ }
+}
+
+void Codec::deInitDecoder() {
+ if (mDecoderBuf) {
+ free(mDecoderBuf);
+ mDecoderBuf = nullptr;
+ }
+ delete mConfig;
+ mConfig = nullptr;
+}
+
+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(const_cast<uint8_t *>(data), size);
+ }
+ delete codec;
+ return 0;
+}
diff --git a/media/libstagefright/foundation/Android.bp b/media/libstagefright/foundation/Android.bp
index effbb4e..5c927e6 100644
--- a/media/libstagefright/foundation/Android.bp
+++ b/media/libstagefright/foundation/Android.bp
@@ -23,7 +23,6 @@
],
header_libs: [
- "libhardware_headers",
"libstagefright_foundation_headers",
"media_ndk_headers",
"media_plugin_headers",
diff --git a/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsTestEnvironment.h b/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsTestEnvironment.h
new file mode 100644
index 0000000..b28a7bc
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsTestEnvironment.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 __AVC_UTILS_TEST_ENVIRONMENT_H__
+#define __AVC_UTILS_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class AVCUtilsTestEnvironment : public::testing::Environment {
+ public:
+ AVCUtilsTestEnvironment() : 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 AVCUtilsTestEnvironment::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 // __AVC_UTILS_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsUnitTest.cpp b/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsUnitTest.cpp
new file mode 100644
index 0000000..77a8599
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsUnitTest.cpp
@@ -0,0 +1,411 @@
+/*
+ * 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 "AVCUtilsUnitTest"
+#include <utils/Log.h>
+
+#include <fstream>
+
+#include "media/stagefright/foundation/ABitReader.h"
+#include "media/stagefright/foundation/avc_utils.h"
+
+#include "AVCUtilsTestEnvironment.h"
+
+constexpr size_t kSmallBufferSize = 2;
+constexpr uint8_t kSPSmask = 0x1f;
+constexpr uint8_t kSPSStartCode = 0x07;
+constexpr uint8_t kConfigVersion = 0x01;
+
+using namespace android;
+
+static AVCUtilsTestEnvironment *gEnv = nullptr;
+
+class MpegAudioUnitTest
+ : public ::testing::TestWithParam<
+ tuple</*audioHeader*/ uint32_t, /*frameSize*/ int32_t, /*sampleRate*/ int32_t,
+ /*numChannels*/ int32_t, /*bitRate*/ int32_t, /*numSamples*/ int32_t>> {};
+
+class VOLDimensionTest
+ : public ::testing::TestWithParam<
+ tuple</*fileName*/ string, /*volWidth*/ int32_t, /*volHeight*/ int32_t>> {};
+
+class AVCUtils {
+ public:
+ bool SetUpAVCUtils(string fileName, string infoFileName) {
+ mInputFile = gEnv->getRes() + fileName;
+ mInputFileStream.open(mInputFile, ifstream::in);
+ if (!mInputFileStream.is_open()) return false;
+
+ mInfoFile = gEnv->getRes() + infoFileName;
+ mInfoFileStream.open(mInfoFile, ifstream::in);
+ if (!mInputFileStream.is_open()) return false;
+ return true;
+ }
+
+ ~AVCUtils() {
+ if (mInputFileStream.is_open()) mInputFileStream.close();
+ if (mInfoFileStream.is_open()) mInfoFileStream.close();
+ }
+
+ string mInputFile;
+ string mInfoFile;
+
+ ifstream mInputFileStream;
+ ifstream mInfoFileStream;
+};
+
+class AVCDimensionTest
+ : public AVCUtils,
+ public ::testing::TestWithParam<
+ tuple</*fileName*/ string, /*infoFileName*/ string,
+ /*avcWidth*/ size_t, /*avcHeight*/ size_t, /*numberOfNALUnits*/ int32_t>> {
+ public:
+ virtual void SetUp() override {
+ tuple<string, string, size_t, size_t, size_t> params = GetParam();
+ string fileName = get<0>(params);
+ string infoFileName = get<1>(params);
+ AVCUtils::SetUpAVCUtils(fileName, infoFileName);
+
+ mFrameWidth = get<2>(params);
+ mFrameHeight = get<3>(params);
+ mNalUnitsExpected = get<4>(params);
+ }
+
+ size_t mFrameWidth;
+ size_t mFrameHeight;
+ int32_t mNalUnitsExpected;
+};
+
+class AvccBoxTest : public AVCDimensionTest {
+ public:
+ virtual void SetUp() override { AVCDimensionTest::SetUp(); }
+};
+
+class AVCFrameTest
+ : public AVCUtils,
+ public ::testing::TestWithParam<pair</*fileName*/ string, /*infoFileName*/ string>> {
+ public:
+ virtual void SetUp() override {
+ string fileName = GetParam().first;
+ string infoFileName = GetParam().second;
+ AVCUtils::SetUpAVCUtils(fileName, infoFileName);
+ }
+};
+
+TEST_P(MpegAudioUnitTest, AudioProfileTest) {
+ tuple<uint32_t, size_t, int, int, int, int> params = GetParam();
+ uint32_t header = get<0>(params);
+
+ size_t audioFrameSize = get<1>(params);
+ int audioSampleRate = get<2>(params);
+ int audioNumChannels = get<3>(params);
+ int audioBitRate = get<4>(params);
+ int audioNumSamples = get<5>(params);
+
+ size_t frameSize = 0;
+ int sampleRate = 0;
+ int numChannels = 0;
+ int bitRate = 0;
+ int numSamples = 0;
+
+ bool status = GetMPEGAudioFrameSize(header, &frameSize, &sampleRate, &numChannels, &bitRate,
+ &numSamples);
+ ASSERT_TRUE(status) << "Failed to get Audio properties";
+
+ ASSERT_EQ(frameSize, audioFrameSize) << "Wrong frame size found";
+
+ ASSERT_EQ(sampleRate, audioSampleRate) << "Wrong sample rate found";
+
+ ASSERT_EQ(numChannels, audioNumChannels) << "Wrong number of channels found";
+
+ ASSERT_EQ(bitRate, audioBitRate) << "Wrong bit rate found";
+
+ ASSERT_EQ(numSamples, audioNumSamples) << "Wrong number of samples found";
+}
+
+TEST_P(VOLDimensionTest, DimensionTest) {
+ tuple<string, int32_t, int32_t> params = GetParam();
+ string inputFile = gEnv->getRes() + get<0>(params);
+ ifstream inputFileStream;
+ inputFileStream.open(inputFile, ifstream::in);
+ ASSERT_TRUE(inputFileStream.is_open()) << "Failed to open: " << inputFile;
+
+ struct stat buf;
+ int8_t err = stat(inputFile.c_str(), &buf);
+ ASSERT_EQ(err, 0) << "Failed to get information for file: " << inputFile;
+
+ size_t fileSize = buf.st_size;
+ ASSERT_NE(fileSize, 0) << "Invalid file size found";
+
+ const uint8_t *volBuffer = new uint8_t[fileSize];
+ ASSERT_NE(volBuffer, nullptr) << "Failed to allocate VOL buffer of size: " << fileSize;
+
+ inputFileStream.read((char *)(volBuffer), fileSize);
+ ASSERT_EQ(inputFileStream.gcount(), fileSize)
+ << "Failed to read complete file, bytes read: " << inputFileStream.gcount();
+
+ int32_t width = get<1>(params);
+ int32_t height = get<2>(params);
+ int32_t volWidth = -1;
+ int32_t volHeight = -1;
+
+ bool status = ExtractDimensionsFromVOLHeader(volBuffer, fileSize, &volWidth, &volHeight);
+ ASSERT_TRUE(status)
+ << "Failed to get VOL dimensions from function: ExtractDimensionsFromVOLHeader()";
+
+ ASSERT_EQ(volWidth, width) << "Expected width: " << width << "Found: " << volWidth;
+
+ ASSERT_EQ(volHeight, height) << "Expected height: " << height << "Found: " << volHeight;
+
+ delete[] volBuffer;
+}
+
+TEST_P(AVCDimensionTest, DimensionTest) {
+ int32_t numNalUnits = 0;
+ int32_t avcWidth = -1;
+ int32_t avcHeight = -1;
+ string line;
+ string type;
+ size_t chunkLength;
+ while (getline(mInfoFileStream, line)) {
+ istringstream stringLine(line);
+ stringLine >> type >> chunkLength;
+ ASSERT_GT(chunkLength, 0) << "Length of the data chunk must be greater than zero";
+
+ const uint8_t *data = new uint8_t[chunkLength];
+ ASSERT_NE(data, nullptr) << "Failed to create a data buffer of size: " << chunkLength;
+
+ const uint8_t *nalStart;
+ size_t nalSize;
+
+ mInputFileStream.read((char *)data, chunkLength);
+ ASSERT_EQ(mInputFileStream.gcount(), chunkLength)
+ << "Failed to read complete file, bytes read: " << mInputFileStream.gcount();
+
+ size_t smallBufferSize = kSmallBufferSize;
+ const uint8_t *sanityData = new uint8_t[smallBufferSize];
+ memcpy((void *)sanityData, (void *)data, smallBufferSize);
+
+ status_t result = getNextNALUnit(&sanityData, &smallBufferSize, &nalStart, &nalSize, true);
+ ASSERT_EQ(result, -EAGAIN) << "Invalid result found when wrong NAL unit passed";
+
+ while (!getNextNALUnit(&data, &chunkLength, &nalStart, &nalSize, true)) {
+ numNalUnits++;
+ // Check if it's an SPS
+ if ((nalStart[0] & kSPSmask) != kSPSStartCode) continue;
+ ASSERT_TRUE(nalSize > 0) << "NAL unit size must be greater than 0";
+
+ sp<ABuffer> spsBuffer = new ABuffer(nalSize);
+ ASSERT_NE(spsBuffer, nullptr) << "ABuffer returned null for size: " << nalSize;
+
+ memcpy(spsBuffer->data(), nalStart, nalSize);
+ FindAVCDimensions(spsBuffer, &avcWidth, &avcHeight);
+ spsBuffer.clear();
+ ASSERT_EQ(avcWidth, mFrameWidth)
+ << "Expected width: " << mFrameWidth << "Found: " << avcWidth;
+
+ ASSERT_EQ(avcHeight, mFrameHeight)
+ << "Expected height: " << mFrameHeight << "Found: " << avcHeight;
+ }
+ delete[] data;
+ }
+ if (mNalUnitsExpected < 0) {
+ ASSERT_GT(numNalUnits, 0) << "Failed to find an NAL Unit";
+ } else {
+ ASSERT_EQ(numNalUnits, mNalUnitsExpected)
+ << "Expected number of NAL units: " << mNalUnitsExpected
+ << " found: " << numNalUnits;
+ }
+}
+
+TEST_P(AvccBoxTest, AvccBoxValidationTest) {
+ int32_t avcWidth = -1;
+ int32_t avcHeight = -1;
+ int32_t accessUnitLength = 0;
+ int32_t profile = -1;
+ int32_t level = -1;
+ string line;
+ string type;
+ size_t chunkLength;
+ while (getline(mInfoFileStream, line)) {
+ istringstream stringLine(line);
+ stringLine >> type >> chunkLength;
+
+ if (type.compare("SPS") && type.compare("PPS")) continue;
+ ASSERT_GT(chunkLength, 0) << "Length of the data chunk must be greater than zero";
+
+ accessUnitLength += chunkLength;
+
+ if (!type.compare("SPS")) {
+ const uint8_t *data = new uint8_t[chunkLength];
+ ASSERT_NE(data, nullptr) << "Failed to create a data buffer of size: " << chunkLength;
+
+ const uint8_t *nalStart;
+ size_t nalSize;
+
+ mInputFileStream.read((char *)data, (uint32_t)chunkLength);
+ ASSERT_EQ(mInputFileStream.gcount(), chunkLength)
+ << "Failed to read complete file, bytes read: " << mInputFileStream.gcount();
+
+ while (!getNextNALUnit(&data, &chunkLength, &nalStart, &nalSize, true)) {
+ // Check if it's an SPS
+ ASSERT_TRUE(nalSize > 0 && (nalStart[0] & kSPSmask) == kSPSStartCode)
+ << "Failed to get SPS";
+
+ ASSERT_GE(nalSize, 4) << "SPS size must be greater than or equal to 4";
+
+ profile = nalStart[1];
+ level = nalStart[3];
+ }
+ delete[] data;
+ }
+ }
+ const uint8_t *accessUnitData = new uint8_t[accessUnitLength];
+ ASSERT_NE(accessUnitData, nullptr) << "Failed to create a buffer of size: " << accessUnitLength;
+
+ mInputFileStream.seekg(0, ios::beg);
+ mInputFileStream.read((char *)accessUnitData, accessUnitLength);
+ ASSERT_EQ(mInputFileStream.gcount(), accessUnitLength)
+ << "Failed to read complete file, bytes read: " << mInputFileStream.gcount();
+
+ sp<ABuffer> accessUnit = new ABuffer(accessUnitLength);
+ ASSERT_NE(accessUnit, nullptr)
+ << "Failed to create an android data buffer of size: " << accessUnitLength;
+
+ memcpy(accessUnit->data(), accessUnitData, accessUnitLength);
+ sp<ABuffer> csdDataBuffer = MakeAVCCodecSpecificData(accessUnit, &avcWidth, &avcHeight);
+ ASSERT_NE(csdDataBuffer, nullptr) << "No data returned from MakeAVCCodecSpecificData()";
+
+ ASSERT_EQ(avcWidth, mFrameWidth) << "Expected width: " << mFrameWidth << "Found: " << avcWidth;
+
+ ASSERT_EQ(avcHeight, mFrameHeight)
+ << "Expected height: " << mFrameHeight << "Found: " << avcHeight;
+
+ uint8_t *csdData = csdDataBuffer->data();
+ ASSERT_EQ(*csdData, kConfigVersion) << "Invalid configuration version";
+
+ ASSERT_GE(csdDataBuffer->size(), 4) << "CSD data size must be greater than or equal to 4";
+
+ ASSERT_EQ(*(csdData + 1), profile)
+ << "Expected AVC profile: " << profile << " found: " << *(csdData + 1);
+
+ ASSERT_EQ(*(csdData + 3), level)
+ << "Expected AVC level: " << level << " found: " << *(csdData + 3);
+ csdDataBuffer.clear();
+ delete[] accessUnitData;
+ accessUnit.clear();
+}
+
+TEST_P(AVCFrameTest, FrameTest) {
+ string line;
+ string type;
+ size_t chunkLength;
+ int32_t frameLayerID;
+ while (getline(mInfoFileStream, line)) {
+ uint32_t layerID = 0;
+ istringstream stringLine(line);
+ stringLine >> type >> chunkLength >> frameLayerID;
+ ASSERT_GT(chunkLength, 0) << "Length of the data chunk must be greater than zero";
+
+ char *data = new char[chunkLength];
+ ASSERT_NE(data, nullptr) << "Failed to allocation data buffer of size: " << chunkLength;
+
+ mInputFileStream.read(data, chunkLength);
+ ASSERT_EQ(mInputFileStream.gcount(), chunkLength)
+ << "Failed to read complete file, bytes read: " << mInputFileStream.gcount();
+
+ if (!type.compare("IDR")) {
+ bool isIDR = IsIDR((uint8_t *)data, chunkLength);
+ ASSERT_TRUE(isIDR);
+
+ layerID = FindAVCLayerId((uint8_t *)data, chunkLength);
+ ASSERT_EQ(layerID, frameLayerID) << "Wrong layer ID found";
+ } else if (!type.compare("P") || !type.compare("B")) {
+ sp<ABuffer> accessUnit = new ABuffer(chunkLength);
+ ASSERT_NE(accessUnit, nullptr) << "Unable to create access Unit";
+
+ memcpy(accessUnit->data(), data, chunkLength);
+ bool isReferenceFrame = IsAVCReferenceFrame(accessUnit);
+ ASSERT_TRUE(isReferenceFrame);
+
+ accessUnit.clear();
+ layerID = FindAVCLayerId((uint8_t *)data, chunkLength);
+ ASSERT_EQ(layerID, frameLayerID) << "Wrong layer ID found";
+ }
+ delete[] data;
+ }
+}
+
+INSTANTIATE_TEST_SUITE_P(AVCUtilsTestAll, MpegAudioUnitTest,
+ ::testing::Values(make_tuple(0xFFFB9204, 418, 44100, 2, 128, 1152),
+ make_tuple(0xFFFB7604, 289, 48000, 2, 96, 1152),
+ make_tuple(0xFFFE5604, 164, 48000, 2, 160, 384)));
+
+// Info File contains the type and length for each chunk/frame
+INSTANTIATE_TEST_SUITE_P(
+ AVCUtilsTestAll, AVCDimensionTest,
+ ::testing::Values(make_tuple("crowd_8x8p50f32_200kbps_bp.h264",
+ "crowd_8x8p50f32_200kbps_bp.info", 8, 8, 11),
+ make_tuple("crowd_640x360p24f300_1000kbps_bp.h264",
+ "crowd_640x360p24f300_1000kbps_bp.info", 640, 360, 11),
+ make_tuple("crowd_1280x720p30f300_5000kbps_bp.h264",
+ "crowd_1280x720p30f300_5000kbps_bp.info", 1280, 720, 12),
+ make_tuple("crowd_1920x1080p50f300_12000kbps_bp.h264",
+ "crowd_1920x1080p50f300_12000kbps_bp.info", 1920, 1080, 14),
+ make_tuple("crowd_3840x2160p60f300_68000kbps_bp.h264",
+ "crowd_3840x2160p60f300_68000kbps_bp.info", 3840, 2160, 14)));
+
+// Info File contains the type and length for each chunk/frame
+INSTANTIATE_TEST_SUITE_P(
+ AVCUtilsTestAll, AvccBoxTest,
+ ::testing::Values(make_tuple("crowd_8x8p50f32_200kbps_bp.h264",
+ "crowd_8x8p50f32_200kbps_bp.info", 8, 8, 11),
+ make_tuple("crowd_1280x720p30f300_5000kbps_bp.h264",
+ "crowd_1280x720p30f300_5000kbps_bp.info", 1280, 720, 12),
+ make_tuple("crowd_1920x1080p50f300_12000kbps_bp.h264",
+ "crowd_1920x1080p50f300_12000kbps_bp.info", 1920, 1080, 14)));
+
+// Info File contains the type and length for each chunk/frame
+INSTANTIATE_TEST_SUITE_P(AVCUtilsTestAll, VOLDimensionTest,
+ ::testing::Values(make_tuple("volData_720_480", 720, 480),
+ make_tuple("volData_1280_720", 1280, 720),
+ make_tuple("volData_1920_1080", 1920, 1080)));
+
+// Info File contains the type, length and layer ID for each chunk/frame
+INSTANTIATE_TEST_SUITE_P(AVCUtilsTestAll, AVCFrameTest,
+ ::testing::Values(make_tuple("crowd_8x8p50f32_200kbps_bp.h264",
+ "crowd_8x8p50f32_200kbps_bp.info"),
+ make_tuple("crowd_640x360p24f300_1000kbps_bp.h264",
+ "crowd_640x360p24f300_1000kbps_bp.info"),
+ make_tuple("crowd_1280x720p30f300_5000kbps_bp.h264",
+ "crowd_1280x720p30f300_5000kbps_bp.info"),
+ make_tuple("crowd_1920x1080p50f300_12000kbps_bp.h264",
+ "crowd_1920x1080p50f300_12000kbps_bp.info"),
+ make_tuple("crowd_3840x2160p60f300_68000kbps_bp.h264",
+ "crowd_3840x2160p60f300_68000kbps_bp.info")));
+
+int main(int argc, char **argv) {
+ gEnv = new AVCUtilsTestEnvironment();
+ ::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/foundation/tests/AVCUtils/Android.bp b/media/libstagefright/foundation/tests/AVCUtils/Android.bp
new file mode 100644
index 0000000..5d0e481
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AVCUtils/Android.bp
@@ -0,0 +1,51 @@
+/*
+ * 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: "AVCUtilsUnitTest",
+ gtest: true,
+
+ srcs: [
+ "AVCUtilsUnitTest.cpp",
+ ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright",
+ "libstagefright_foundation",
+ ],
+
+ include_dirs: [
+ "frameworks/av/media/libstagefright/foundation",
+ ],
+
+ cflags: [
+ "-Werror",
+ "-Wall",
+ ],
+
+ sanitize: {
+ cfi: true,
+ misc_undefined: [
+ "unsigned-integer-overflow",
+ "signed-integer-overflow",
+ ],
+ },
+}
diff --git a/media/libstagefright/foundation/tests/AVCUtils/AndroidTest.xml b/media/libstagefright/foundation/tests/AVCUtils/AndroidTest.xml
new file mode 100644
index 0000000..6a088a8
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AVCUtils/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 AVC Utils unit tests">
+ <option name="test-suite-tag" value="AVCUtilsUnitTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="false" />
+ <option name="push" value="AVCUtilsUnitTest->/data/local/tmp/AVCUtilsUnitTest" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsUnitTest.zip?unzip=true"
+ value="/data/local/tmp/AVCUtilsUnitTest/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="AVCUtilsUnitTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/AVCUtilsUnitTest/" />
+ </test>
+</configuration>
diff --git a/media/libstagefright/foundation/tests/AVCUtils/README.md b/media/libstagefright/foundation/tests/AVCUtils/README.md
new file mode 100644
index 0000000..609d72e
--- /dev/null
+++ b/media/libstagefright/foundation/tests/AVCUtils/README.md
@@ -0,0 +1,39 @@
+## Media Testing ##
+---
+#### AVCUtils Test
+The AVC Utility Unit Test Suite validates the avc_utils librariy available in libstagefright/foundation.
+
+Run the following steps to build the test suite:
+```
+m AVCUtilsUnitTest
+```
+
+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/AVCUtilsUnitTest/AVCUtilsUnitTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/AVCUtilsUnitTest/AVCUtilsUnitTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/AVCUtils/AVCUtilsUnitTest.zip). Download, unzip and push these files into device for testing.
+
+```
+adb push AVCUtilsUnitTest /data/local/tmp/
+```
+
+usage: AVCUtilsUnitTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/AVCUtilsUnitTest -P /data/local/tmp/AVCUtilsUnitTest/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest AVCUtilsUnitTest -- --enable-module-dynamic-download=true
+```
diff --git a/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
index d142781..e39c915 100644
--- a/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
+++ b/media/libstagefright/foundation/tests/OpusHeader/OpusHeaderTest.cpp
@@ -89,17 +89,19 @@
class OpusHeaderWriteTest
: public OpusHeaderTest,
- public ::testing::TestWithParam<pair<int32_t /* ChannelCount */, int32_t /* skipSamples */>> {
-};
+ public ::testing::TestWithParam<tuple<int32_t /* ChannelCount */, int32_t /* skipSamples */,
+ string /* referenceFile */>> {};
TEST_P(OpusHeaderWriteTest, WriteTest) {
+ tuple<int32_t, int32_t, string> params = GetParam();
OpusHeader writtenHeader;
memset(&writtenHeader, 0, sizeof(writtenHeader));
- int32_t channels = GetParam().first;
+ int32_t channels = get<0>(params);
writtenHeader.channels = channels;
writtenHeader.num_streams = channels;
writtenHeader.channel_mapping = ((channels > 8) ? 255 : (channels > 2));
- int32_t skipSamples = GetParam().second;
+ int32_t skipSamples = get<1>(params);
+ string referenceFileName = gEnv->getRes() + get<2>(params);
writtenHeader.skip_samples = skipSamples;
uint64_t codecDelayNs = skipSamples * kNsecPerSec / kOpusSampleRate;
uint8_t headerData[kMaxOpusHeaderSize];
@@ -112,11 +114,29 @@
ofstream ostrm;
ostrm.open(OUTPUT_FILE_NAME, ofstream::binary);
ASSERT_TRUE(ostrm.is_open()) << "Failed to open output file " << OUTPUT_FILE_NAME;
-
- // TODO : Validate bitstream (b/150116402)
- ostrm.write(reinterpret_cast<char *>(headerData), sizeof(headerData));
+ ostrm.write(reinterpret_cast<char *>(headerData), headerSize);
ostrm.close();
+ mEleStream.open(referenceFileName, ifstream::binary);
+ ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open referenceFileName " << get<2>(params);
+
+ struct stat buf;
+ int32_t statStatus = stat(referenceFileName.c_str(), &buf);
+ ASSERT_EQ(statStatus, 0) << "Unable to get file properties";
+
+ size_t fileSize = buf.st_size;
+ mInputBuffer = (uint8_t *)malloc(fileSize);
+ ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory. Malloc failed for size " << fileSize;
+
+ mEleStream.read(reinterpret_cast<char *>(mInputBuffer), fileSize);
+ ASSERT_EQ(mEleStream.gcount(), fileSize) << "mEleStream.gcount() != bytesCount";
+
+ ASSERT_EQ(fileSize, headerSize)
+ << "Mismatch in size between header generated and reference header";
+ int32_t match = memcmp(reinterpret_cast<char *>(mInputBuffer),
+ reinterpret_cast<char *>(headerData), fileSize);
+ ASSERT_EQ(match, 0) << "Opus header does not match reference file: " << referenceFileName;
+
size_t opusHeadSize = 0;
size_t codecDelayBufSize = 0;
size_t seekPreRollBufSize = 0;
@@ -266,19 +286,20 @@
}
}
-INSTANTIATE_TEST_SUITE_P(OpusHeaderTestAll, OpusHeaderWriteTest,
- ::testing::Values(make_pair(1, 312),
- make_pair(2, 312),
- make_pair(5, 312),
- make_pair(6, 312),
- make_pair(1, 0),
- make_pair(2, 0),
- make_pair(5, 0),
- make_pair(6, 0),
- make_pair(1, 624),
- make_pair(2, 624),
- make_pair(5, 624),
- make_pair(6, 624)));
+INSTANTIATE_TEST_SUITE_P(
+ OpusHeaderTestAll, OpusHeaderWriteTest,
+ ::testing::Values(make_tuple(1, 312, "output_channels_1skipSamples_312.opus"),
+ make_tuple(2, 312, "output_channels_2skipSamples_312.opus"),
+ make_tuple(5, 312, "output_channels_5skipSamples_312.opus"),
+ make_tuple(6, 312, "output_channels_6skipSamples_312.opus"),
+ make_tuple(1, 0, "output_channels_1skipSamples_0.opus"),
+ make_tuple(2, 0, "output_channels_2skipSamples_0.opus"),
+ make_tuple(5, 0, "output_channels_5skipSamples_0.opus"),
+ make_tuple(6, 0, "output_channels_6skipSamples_0.opus"),
+ make_tuple(1, 624, "output_channels_1skipSamples_624.opus"),
+ make_tuple(2, 624, "output_channels_2skipSamples_624.opus"),
+ make_tuple(5, 624, "output_channels_5skipSamples_624.opus"),
+ make_tuple(6, 624, "output_channels_6skipSamples_624.opus")));
INSTANTIATE_TEST_SUITE_P(
OpusHeaderTestAll, OpusHeaderParseTest,
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/AndroidTest.xml b/media/libstagefright/tests/writer/AndroidTest.xml
index d831555..a21be8a 100644
--- a/media/libstagefright/tests/writer/AndroidTest.xml
+++ b/media/libstagefright/tests/writer/AndroidTest.xml
@@ -19,12 +19,13 @@
<option name="cleanup" value="true" />
<option name="push" value="writerTest->/data/local/tmp/writerTest" />
<option name="push-file"
- key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/Writer.zip?unzip=true"
- value="/data/local/tmp/writerTestRes/" />
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/WriterTestRes.zip?unzip=true"
+ value="/data/local/tmp/WriterTestRes/" />
</target_preparer>
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
<option name="module-name" value="writerTest" />
- <option name="native-test-flag" value="-P /data/local/tmp/writerTestRes/" />
+ <option name="native-test-flag" value="-P /data/local/tmp/WriterTestRes/" />
+ <option name="native-test-flag" value="-C true" />
</test>
</configuration>
diff --git a/media/libstagefright/tests/writer/README.md b/media/libstagefright/tests/writer/README.md
index ae07917..e103613 100644
--- a/media/libstagefright/tests/writer/README.md
+++ b/media/libstagefright/tests/writer/README.md
@@ -19,13 +19,18 @@
adb push ${OUT}/data/nativetest/writerTest/writerTest /data/local/tmp/
-The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/writerTestRes.zip).
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/tests/writer/WriterTestRes.zip).
Download and extract the folder. Push all the files in this folder to /data/local/tmp/ on the device.
```
-adb push writerTestRes /data/local/tmp/
+adb push WriterTestRes /data/local/tmp/
```
-usage: writerTest -P \<path_to_res_folder\>
+usage: writerTest -P \<path_to_res_folder\> -C <remove_output_file>
```
-adb shell /data/local/tmp/writerTest -P /data/local/tmp/
+adb shell /data/local/tmp/writerTest -P /data/local/tmp/WriterTestRes/ -C true
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest writerTest -- --enable-module-dynamic-download=true
```
diff --git a/media/libstagefright/tests/writer/WriterTest.cpp b/media/libstagefright/tests/writer/WriterTest.cpp
index 3fa2aa6..4d8df2d 100644
--- a/media/libstagefright/tests/writer/WriterTest.cpp
+++ b/media/libstagefright/tests/writer/WriterTest.cpp
@@ -107,6 +107,7 @@
}
mBufferInfo.clear();
if (mInputStream.is_open()) mInputStream.close();
+ if (gEnv->cleanUp()) remove(OUTPUT_FILE_NAME);
}
void setupWriterType(string writerFormat) {
diff --git a/media/libstagefright/tests/writer/WriterTestEnvironment.h b/media/libstagefright/tests/writer/WriterTestEnvironment.h
index 99e686f..7da0a62 100644
--- a/media/libstagefright/tests/writer/WriterTestEnvironment.h
+++ b/media/libstagefright/tests/writer/WriterTestEnvironment.h
@@ -25,7 +25,7 @@
class WriterTestEnvironment : public ::testing::Environment {
public:
- WriterTestEnvironment() : res("/data/local/tmp/") {}
+ WriterTestEnvironment() : res("/data/local/tmp/"), deleteOutput(true) {}
// Parses the command line arguments
int initFromOptions(int argc, char **argv);
@@ -34,16 +34,21 @@
const string getRes() const { return res; }
+ bool cleanUp() const { return deleteOutput; }
+
private:
string res;
+ bool deleteOutput;
};
int WriterTestEnvironment::initFromOptions(int argc, char **argv) {
- static struct option options[] = {{"res", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+ static struct option options[] = {{"res", required_argument, 0, 'P'},
+ {"cleanUp", optional_argument, 0, 'C'},
+ {0, 0, 0, 0}};
while (true) {
int index = 0;
- int c = getopt_long(argc, argv, "P:", options, &index);
+ int c = getopt_long(argc, argv, "P:C:", options, &index);
if (c == -1) {
break;
}
@@ -52,6 +57,11 @@
case 'P':
setRes(optarg);
break;
+ case 'C':
+ if (!strcmp(optarg, "false")) {
+ deleteOutput = false;
+ }
+ break;
default:
break;
}
@@ -62,7 +72,8 @@
"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",
+ "-P, --path: Resource files directory location\n"
+ "-C, default:true. Delete output file after test completes\n",
argv[optind ?: 1], argv[0]);
return 2;
}
diff --git a/media/libstagefright/timedtext/TextDescriptions.cpp b/media/libstagefright/timedtext/TextDescriptions.cpp
index 6c94754..2c2d11d 100644
--- a/media/libstagefright/timedtext/TextDescriptions.cpp
+++ b/media/libstagefright/timedtext/TextDescriptions.cpp
@@ -504,7 +504,7 @@
return OK;
}
- parcel->write(tmpData, len);
+ parcel->writeByteArray(len, tmpData);
tmpData += len;
subChunkRemaining -= len;
}
diff --git a/media/libstagefright/timedtext/test/AndroidTest.xml b/media/libstagefright/timedtext/test/AndroidTest.xml
index 8925790..3654e23 100644
--- a/media/libstagefright/timedtext/test/AndroidTest.xml
+++ b/media/libstagefright/timedtext/test/AndroidTest.xml
@@ -25,7 +25,7 @@
<test class="com.android.tradefed.testtype.GTest" >
<option name="native-test-device-path" value="/data/local/tmp" />
- <option name="module-name" value="TimedTextUnitTestt" />
+ <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/TimedTextUnitTest.cpp b/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
index 64379d6..d85ae39 100644
--- a/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
+++ b/media/libstagefright/timedtext/test/TimedTextUnitTest.cpp
@@ -346,6 +346,8 @@
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),
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index a232150..3be5e74 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -1525,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/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/ndk/Android.bp b/media/ndk/Android.bp
index 24cad4d..4b60475 100644
--- a/media/ndk/Android.bp
+++ b/media/ndk/Android.bp
@@ -35,7 +35,13 @@
cc_library_headers {
name: "media_ndk_headers",
vendor_available: true,
- export_include_dirs: ["include"]
+ export_include_dirs: ["include"],
+ host_supported: true,
+ target: {
+ darwin: {
+ enabled: false,
+ },
+ },
}
cc_library_shared {
@@ -70,6 +76,7 @@
],
header_libs: [
+ "jni_headers",
"libmediadrm_headers",
],
@@ -97,6 +104,8 @@
"libmediandk_utils",
],
+ export_header_lib_headers: ["jni_headers"],
+
export_include_dirs: ["include"],
export_shared_lib_headers: [
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/audiopolicy/engine/config/include/EngineConfig.h b/services/audiopolicy/engine/config/include/EngineConfig.h
index 7f5ed5e..5d22c24 100644
--- a/services/audiopolicy/engine/config/include/EngineConfig.h
+++ b/services/audiopolicy/engine/config/include/EngineConfig.h
@@ -31,9 +31,6 @@
/** Default path of audio policy usages configuration file. */
constexpr char DEFAULT_PATH[] = "/vendor/etc/audio_policy_engine_configuration.xml";
-/** Directories where the effect libraries will be search for. */
-constexpr const char* POLICY_USAGE_LIBRARY_PATH[] = {"/odm/etc/", "/vendor/etc/", "/system/etc/"};
-
using AttributesVector = std::vector<audio_attributes_t>;
using StreamVector = std::vector<audio_stream_type_t>;
diff --git a/services/audiopolicy/engine/config/src/EngineConfig.cpp b/services/audiopolicy/engine/config/src/EngineConfig.cpp
index 7f8cdd9..4842cb2 100644
--- a/services/audiopolicy/engine/config/src/EngineConfig.cpp
+++ b/services/audiopolicy/engine/config/src/EngineConfig.cpp
@@ -21,6 +21,7 @@
#include <cutils/properties.h>
#include <media/TypeConverter.h>
#include <media/convert.h>
+#include <system/audio_config.h>
#include <utils/Log.h>
#include <libxml/parser.h>
#include <libxml/xinclude.h>
@@ -693,9 +694,6 @@
return deserializeLegacyVolumeCollection(doc, cur, volumeGroups, nbSkippedElements);
}
-static const char *kConfigLocationList[] = {"/odm/etc", "/vendor/etc", "/system/etc"};
-static const int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
static const int gApmXmlConfigFilePathMaxLength = 128;
static constexpr const char *apmXmlConfigFileName = "audio_policy_configuration.xml";
@@ -715,9 +713,9 @@
fileNames.push_back(apmXmlConfigFileName);
for (const char* fileName : fileNames) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
+ for (const auto& path : audio_get_configuration_paths()) {
snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
- "%s/%s", kConfigLocationList[i], fileName);
+ "%s/%s", path.c_str(), fileName);
ret = parseLegacyVolumeFile(audioPolicyXmlConfigFile, volumeGroups);
if (ret == NO_ERROR) {
return ret;
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index a72da83..2a2e449 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -48,6 +48,7 @@
#include <private/android_filesystem_config.h>
#include <soundtrigger/SoundTrigger.h>
#include <system/audio.h>
+#include <system/audio_config.h>
#include "AudioPolicyManager.h"
#include <Serializer.h>
#include "TypeConverter.h"
@@ -3708,6 +3709,22 @@
removeAudioPatch(patchDesc->getHandle());
nextAudioPortGeneration();
mpClientInterface->onAudioPatchListUpdate();
+ // SW Bridge
+ if (patch->num_sources > 1 && patch->sources[1].type == AUDIO_PORT_TYPE_MIX) {
+ sp<SwAudioOutputDescriptor> outputDesc =
+ mOutputs.getOutputFromId(patch->sources[1].id);
+ if (outputDesc == NULL) {
+ ALOGE("%s output not found for id %d", __func__, patch->sources[0].id);
+ return BAD_VALUE;
+ }
+ // Reset handle so that setOutputDevice will force new AF patch to reach the sink
+ outputDesc->setPatchHandle(AUDIO_PATCH_HANDLE_NONE);
+ setOutputDevices(outputDesc,
+ getNewOutputDevices(outputDesc, true /*fromCache*/),
+ true, /*force*/
+ 0,
+ NULL);
+ }
} else {
return BAD_VALUE;
}
@@ -4296,12 +4313,6 @@
return mAudioPortGeneration++;
}
-// Treblized audio policy xml config will be located in /odm/etc or /vendor/etc.
-static const char *kConfigLocationList[] =
- {"/odm/etc", "/vendor/etc", "/system/etc"};
-static const int kConfigLocationListSize =
- (sizeof(kConfigLocationList) / sizeof(kConfigLocationList[0]));
-
static status_t deserializeAudioPolicyXmlConfig(AudioPolicyConfig &config) {
char audioPolicyXmlConfigFile[AUDIO_POLICY_XML_CONFIG_FILE_PATH_MAX_LENGTH];
std::vector<const char*> fileNames;
@@ -4323,9 +4334,9 @@
fileNames.push_back(AUDIO_POLICY_XML_CONFIG_FILE_NAME);
for (const char* fileName : fileNames) {
- for (int i = 0; i < kConfigLocationListSize; i++) {
+ for (const auto& path : audio_get_configuration_paths()) {
snprintf(audioPolicyXmlConfigFile, sizeof(audioPolicyXmlConfigFile),
- "%s/%s", kConfigLocationList[i], fileName);
+ "%s/%s", path.c_str(), fileName);
ret = deserializeAudioPolicyFile(audioPolicyXmlConfigFile, &config);
if (ret == NO_ERROR) {
config.setSource(audioPolicyXmlConfigFile);
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.h b/services/audiopolicy/managerdefault/AudioPolicyManager.h
index 9eb9a1d..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
{
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"