Log additional NuPlayer information in bug reports am: 9aacf19579 am: bf4e3ecaba
Original change: https://googleplex-android-review.googlesource.com/c/platform/frameworks/av/+/21920683
Change-Id: I3c43b1eb694cf89f47894718fa4febcff1d2af9e
Signed-off-by: Automerger Merge Worker <android-build-automerger-merge-worker@system.gserviceaccount.com>
diff --git a/media/codec2/components/gav1/C2SoftGav1Dec.cpp b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
index 8aed623..914167d 100644
--- a/media/codec2/components/gav1/C2SoftGav1Dec.cpp
+++ b/media/codec2/components/gav1/C2SoftGav1Dec.cpp
@@ -790,6 +790,18 @@
getHDRStaticParams(buffer, work);
getHDR10PlusInfoData(buffer, work);
+#if LIBYUV_VERSION < 1779
+ if (buffer->bitdepth == 10 &&
+ !(buffer->image_format == libgav1::kImageFormatYuv420 ||
+ buffer->image_format == libgav1::kImageFormatMonochrome400)) {
+ ALOGE("image_format %d not supported for 10bit", buffer->image_format);
+ mSignalledError = true;
+ work->workletsProcessed = 1u;
+ work->result = C2_CORRUPTED;
+ return false;
+ }
+#endif
+
const bool isMonochrome =
buffer->image_format == libgav1::kImageFormatMonochrome400;
@@ -886,6 +898,7 @@
dstYStride /= 2;
dstUStride /= 2;
dstVStride /= 2;
+#if LIBYUV_VERSION >= 1779
if (buffer->image_format == libgav1::kImageFormatYuv444 ||
buffer->image_format == libgav1::kImageFormatYuv422) {
// TODO(https://crbug.com/libyuv/952): replace this block with libyuv::I410ToP010 and
@@ -918,7 +931,13 @@
srcYStride, srcUStride, srcVStride, dstYStride,
dstUStride, mWidth, mHeight, isMonochrome);
}
+#else // LIBYUV_VERSION < 1779
+ convertYUV420Planar16ToP010((uint16_t *)dstY, (uint16_t *)dstU, srcY, srcU, srcV,
+ srcYStride, srcUStride, srcVStride, dstYStride,
+ dstUStride, mWidth, mHeight, isMonochrome);
+#endif // LIBYUV_VERSION >= 1779
} else {
+#if LIBYUV_VERSION >= 1779
if (buffer->image_format == libgav1::kImageFormatYuv444) {
// TODO(https://crbug.com/libyuv/950): replace this block with libyuv::I410ToI420 when
// it's available.
@@ -942,10 +961,15 @@
dstY, dstYStride, dstU, dstUStride, dstV, dstVStride,
mWidth, mHeight);
} else {
- convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride / 2,
- srcUStride / 2, srcVStride / 2, dstYStride, dstUStride,
+ convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+ srcUStride, srcVStride, dstYStride, dstUStride,
mWidth, mHeight, isMonochrome);
}
+#else // LIBYUV_VERSION < 1779
+ convertYUV420Planar16ToYV12(dstY, dstU, dstV, srcY, srcU, srcV, srcYStride,
+ srcUStride, srcVStride, dstYStride, dstUStride,
+ mWidth, mHeight, isMonochrome);
+#endif // LIBYUV_VERSION >= 1779
}
} else {
const uint8_t *srcY = (const uint8_t *)buffer->plane[0];
diff --git a/media/codec2/vndk/C2AllocatorGralloc.cpp b/media/codec2/vndk/C2AllocatorGralloc.cpp
index f272499..0803dc3 100644
--- a/media/codec2/vndk/C2AllocatorGralloc.cpp
+++ b/media/codec2/vndk/C2AllocatorGralloc.cpp
@@ -23,6 +23,7 @@
#include <aidl/android/hardware/graphics/common/PlaneLayoutComponentType.h>
#include <android/hardware/graphics/common/1.2/types.h>
#include <cutils/native_handle.h>
+#include <drm/drm_fourcc.h>
#include <gralloctypes/Gralloc4.h>
#include <hardware/gralloc.h>
#include <ui/GraphicBufferAllocator.h>
@@ -478,7 +479,25 @@
// 'NATIVE' on Android means LITTLE_ENDIAN
constexpr C2PlaneInfo::endianness_t kEndianness = C2PlaneInfo::NATIVE;
- switch (mFormat) {
+ // Try to resolve IMPLEMENTATION_DEFINED format to accurate format if
+ // possible.
+ uint32_t format = mFormat;
+ uint32_t fourCc;
+ if (format == HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED &&
+ !GraphicBufferMapper::get().getPixelFormatFourCC(mBuffer, &fourCc)) {
+ switch (fourCc) {
+ case DRM_FORMAT_XBGR8888:
+ format = static_cast<uint32_t>(PixelFormat4::RGBX_8888);
+ break;
+ case DRM_FORMAT_ABGR8888:
+ format = static_cast<uint32_t>(PixelFormat4::RGBA_8888);
+ break;
+ default:
+ break;
+ }
+ }
+
+ switch (format) {
case static_cast<uint32_t>(PixelFormat4::RGBA_1010102): {
// TRICKY: this is used for media as YUV444 in the case when it is queued directly to a
// Surface. In all other cases it is RGBA. We don't know which case it is here, so
diff --git a/media/libaudioclient/AudioSystem.cpp b/media/libaudioclient/AudioSystem.cpp
index 28d76d7..7116b82 100644
--- a/media/libaudioclient/AudioSystem.cpp
+++ b/media/libaudioclient/AudioSystem.cpp
@@ -132,12 +132,10 @@
binder = gAudioFlingerBinder;
} else {
sp<IServiceManager> sm = defaultServiceManager();
- do {
- binder = sm->getService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
- if (binder != nullptr) break;
- ALOGW("AudioFlinger not published, waiting...");
- usleep(500000); // 0.5 s
- } while (true);
+ binder = sm->waitForService(String16(IAudioFlinger::DEFAULT_SERVICE_NAME));
+ if (binder == nullptr) {
+ return nullptr;
+ }
}
binder->linkToDeath(gAudioFlingerClient);
const auto afs = interface_cast<media::IAudioFlingerService>(binder);
@@ -864,14 +862,10 @@
Mutex::Autolock _l(gLockAPS);
if (gAudioPolicyService == 0) {
sp<IServiceManager> sm = defaultServiceManager();
- sp<IBinder> binder;
- do {
- binder = sm->getService(String16("media.audio_policy"));
- if (binder != 0)
- break;
- ALOGW("AudioPolicyService not published, waiting...");
- usleep(500000); // 0.5 s
- } while (true);
+ sp<IBinder> binder = sm->waitForService(String16("media.audio_policy"));
+ if (binder == nullptr) {
+ return nullptr;
+ }
if (gAudioPolicyServiceClient == NULL) {
gAudioPolicyServiceClient = new AudioPolicyServiceClient();
}
@@ -2078,8 +2072,7 @@
return BAD_VALUE;
}
- const sp<IAudioPolicyService>
- & aps = AudioSystem::get_audio_policy_service();
+ const sp<IAudioPolicyService>& aps = AudioSystem::get_audio_policy_service();
if (aps == 0) return PERMISSION_DENIED;
std::vector<AudioFormatDescription> formatsAidl;
diff --git a/media/libstagefright/colorconversion/ColorConverter.cpp b/media/libstagefright/colorconversion/ColorConverter.cpp
index 5e7a4c4..92c5eb3 100644
--- a/media/libstagefright/colorconversion/ColorConverter.cpp
+++ b/media/libstagefright/colorconversion/ColorConverter.cpp
@@ -340,6 +340,14 @@
return mCropBottom - mCropTop + 1;
}
+bool ColorConverter::BitmapParams::isValid() const {
+ if (!((mStride & 1) == 0 // stride must be even
+ && mStride >= mBpp * cropWidth())) {
+ return false;
+ }
+ return true;
+}
+
status_t ColorConverter::convert(
const void *srcBits,
size_t srcWidth, size_t srcHeight, size_t srcStride,
@@ -359,9 +367,11 @@
dstWidth, dstHeight, dstStride,
dstCropLeft, dstCropTop, dstCropRight, dstCropBottom, mDstFormat);
- if (!((src.mCropLeft & 1) == 0
- && src.cropWidth() == dst.cropWidth()
- && src.cropHeight() == dst.cropHeight())) {
+ if (!(src.isValid()
+ && dst.isValid()
+ && (src.mCropLeft & 1) == 0
+ && src.cropWidth() == dst.cropWidth()
+ && src.cropHeight() == dst.cropHeight())) {
return ERROR_UNSUPPORTED;
}
@@ -466,6 +476,7 @@
}
}
+// Interleaved YUV 422 CbYCrY to RGB565
status_t ColorConverter::convertCbYCrY(
const BitmapParams &src, const BitmapParams &dst) {
// XXX Untested
@@ -488,10 +499,10 @@
+ dst.mCropTop * dst.mWidth + dst.mCropLeft;
const uint8_t *src_ptr = (const uint8_t *)src.mBits
- + (src.mCropTop * dst.mWidth + src.mCropLeft) * 2;
+ + (src.mCropTop * src.mWidth + src.mCropLeft) * 2;
for (size_t y = 0; y < src.cropHeight(); ++y) {
- for (size_t x = 0; x < src.cropWidth(); x += 2) {
+ for (size_t x = 0; x < src.cropWidth() - 1; x += 2) {
signed y1 = (signed)src_ptr[2 * x + 1] - _c16;
signed y2 = (signed)src_ptr[2 * x + 3] - _c16;
signed u = (signed)src_ptr[2 * x] - 128;
@@ -661,13 +672,15 @@
*u = ((uint8_t*)src_u)[x / 2] - 128;
*v = ((uint8_t*)src_v)[x / 2] - 128;
};
+ // this format stores 10 bits content with 16 bits
+ // converting it to 8 bits src
case OMX_COLOR_FormatYUV420Planar16:
return [](void *src_y, void *src_u, void *src_v, size_t x,
signed *y1, signed *y2, signed *u, signed *v) {
- *y1 = (signed)(((uint16_t*)src_y)[x] >> 2);
- *y2 = (signed)(((uint16_t*)src_y)[x + 1] >> 2);
- *u = (signed)(((uint16_t*)src_u)[x / 2] >> 2) - 128;
- *v = (signed)(((uint16_t*)src_v)[x / 2] >> 2) - 128;
+ *y1 = (uint8_t)(((uint16_t*)src_y)[x] >> 2);
+ *y2 = (uint8_t)(((uint16_t*)src_y)[x + 1] >> 2);
+ *u = (uint8_t)(((uint16_t*)src_u)[x / 2] >> 2) - 128;
+ *v = (uint8_t)(((uint16_t*)src_v)[x / 2] >> 2) - 128;
};
default:
TRESPASS();
@@ -1125,46 +1138,25 @@
status_t ColorConverter::convertQCOMYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
- const uint8_t *src_y =
- (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + src.mWidth * src.mHeight
- + src.mCropTop * src.mWidth + src.mCropLeft;
-
/* QCOMYUV420SemiPlanar is NV21, while MediaCodec uses NV12 */
return convertYUV420SemiPlanarBase(
- src, dst, src_y, src_u, src.mWidth /* row_inc */, true /* isNV21 */);
+ src, dst, src.mWidth /* row_inc */, true /* isNV21 */);
}
status_t ColorConverter::convertTIYUV420PackedSemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
- const uint8_t *src_y =
- (const uint8_t *)src.mBits + src.mCropTop * src.mWidth + src.mCropLeft;
-
- const uint8_t *src_u =
- (const uint8_t *)src_y + src.mWidth * (src.mHeight - src.mCropTop / 2);
-
return convertYUV420SemiPlanarBase(
- src, dst, src_y, src_u, src.mWidth /* row_inc */);
+ src, dst, src.mWidth /* row_inc */);
}
status_t ColorConverter::convertYUV420SemiPlanar(
const BitmapParams &src, const BitmapParams &dst) {
- const uint8_t *src_y =
- (const uint8_t *)src.mBits + src.mCropTop * src.mStride + src.mCropLeft;
-
- const uint8_t *src_u =
- (const uint8_t *)src.mBits + src.mHeight * src.mStride +
- (src.mCropTop / 2) * src.mStride + src.mCropLeft;
-
return convertYUV420SemiPlanarBase(
- src, dst, src_y, src_u, src.mStride /* row_inc */);
+ src, dst, src.mStride /* row_inc */);
}
-status_t ColorConverter::convertYUV420SemiPlanarBase(
- const BitmapParams &src, const BitmapParams &dst,
- const uint8_t *src_y, const uint8_t *src_u, size_t row_inc, bool isNV21) {
+status_t ColorConverter::convertYUV420SemiPlanarBase(const BitmapParams &src,
+ const BitmapParams &dst, size_t row_inc, bool isNV21) {
const struct Coeffs *matrix = getMatrix();
if (!matrix) {
return ERROR_UNSUPPORTED;
@@ -1182,6 +1174,12 @@
uint16_t *dst_ptr = (uint16_t *)((uint8_t *)
dst.mBits + dst.mCropTop * dst.mStride + dst.mCropLeft * dst.mBpp);
+ const uint8_t *src_y =
+ (const uint8_t *)src.mBits + src.mCropTop * row_inc + src.mCropLeft;
+
+ const uint8_t *src_u = (const uint8_t *)src.mBits + src.mHeight * row_inc +
+ (src.mCropTop / 2) * row_inc + src.mCropLeft;
+
for (size_t y = 0; y < src.cropHeight(); ++y) {
for (size_t x = 0; x < src.cropWidth(); x += 2) {
signed y1 = (signed)src_y[x] - _c16;
diff --git a/media/libstagefright/colorconversion/fuzzer/color_conversion_fuzzer.cpp b/media/libstagefright/colorconversion/fuzzer/color_conversion_fuzzer.cpp
index 7c2bfe5..b91f7dc 100644
--- a/media/libstagefright/colorconversion/fuzzer/color_conversion_fuzzer.cpp
+++ b/media/libstagefright/colorconversion/fuzzer/color_conversion_fuzzer.cpp
@@ -53,6 +53,7 @@
int32_t height) {
int32_t frameSize;
switch ((int32_t)colorFormat) {
+ case OMX_COLOR_FormatCbYCrY: // Interleaved YUV422
case OMX_COLOR_Format16bitRGB565: {
frameSize = 2 * stride * height;
break;
@@ -71,7 +72,6 @@
}
case OMX_COLOR_FormatYUV420Planar:
case OMX_COLOR_FormatYUV420SemiPlanar:
- case OMX_COLOR_FormatCbYCrY:
case OMX_QCOM_COLOR_FormatYVU420SemiPlanar:
case OMX_TI_COLOR_FormatYUV420PackedSemiPlanar:
default: {
diff --git a/media/libstagefright/include/media/stagefright/ColorConverter.h b/media/libstagefright/include/media/stagefright/ColorConverter.h
index 7a05f00..da3267e 100644
--- a/media/libstagefright/include/media/stagefright/ColorConverter.h
+++ b/media/libstagefright/include/media/stagefright/ColorConverter.h
@@ -74,6 +74,8 @@
size_t cropWidth() const;
size_t cropHeight() const;
+ bool isValid() const;
+
void *mBits;
OMX_COLOR_FORMATTYPE mColorFormat;
size_t mWidth, mHeight;
@@ -121,7 +123,7 @@
status_t convertYUV420SemiPlanarBase(
const BitmapParams &src, const BitmapParams &dst,
- const uint8_t *src_y, const uint8_t *src_u, size_t row_inc, bool isNV21 = false);
+ size_t row_inc, bool isNV21 = false);
status_t convertTIYUV420PackedSemiPlanar(
const BitmapParams &src, const BitmapParams &dst);
diff --git a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
index 67c6102..8c1ef3b 100644
--- a/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
+++ b/media/libstagefright/xmlparser/MediaCodecsXmlParser.cpp
@@ -19,6 +19,8 @@
#include <media/stagefright/xmlparser/MediaCodecsXmlParser.h>
+#include <android/api-level.h>
+
#include <android-base/logging.h>
#include <android-base/macros.h>
#include <android-base/properties.h>
@@ -30,6 +32,7 @@
#include <expat.h>
#include <stdio.h>
+#include <stdlib.h>
#include <string.h>
#include <sys/stat.h>
@@ -360,7 +363,7 @@
status_t updateMediaCodec(
const char *rank, const StringSet &domain, const StringSet &variants,
- const char *enabled);
+ const char *enabled, const char *minsdk);
};
status_t parseXmlFilesInSearchDirs(
@@ -493,6 +496,9 @@
}
}
+// current SDK for this device; filled in when initializing the parser.
+static int mysdk = 0;
+
MediaCodecsXmlParser::Impl::Parser::Parser(State *state, std::string path)
: mState(state),
mPath(path),
@@ -502,6 +508,20 @@
if (end != std::string::npos) {
mHrefBase = path.substr(0, end + 1);
}
+
+#if defined(__ANDROID_API_U__)
+ // this is sdk calculation is intended only for devices >= U
+ static std::once_flag sCheckOnce;
+
+ std::call_once(sCheckOnce, [&](){
+ mysdk = android_get_device_api_level();
+
+ // work around main development branch being on same SDK as the last dessert release.
+ if (__ANDROID_API__ == __ANDROID_API_FUTURE__) {
+ mysdk++;
+ }
+ });
+#endif // __ANDROID_API_U__
}
void MediaCodecsXmlParser::Impl::Parser::parseXmlFile() {
@@ -930,6 +950,7 @@
const char *a_domain = nullptr;
const char *a_variant = nullptr;
const char *a_enabled = nullptr;
+ const char *a_minsdk = nullptr;
size_t i = 0;
while (attrs[i] != nullptr) {
@@ -953,6 +974,8 @@
a_variant = attrs[++i];
} else if (strEq(attrs[i], "enabled")) {
a_enabled = attrs[++i];
+ } else if (strEq(attrs[i], "minsdk")) {
+ a_minsdk = attrs[++i];
} else {
PLOGD("MediaCodec: ignoring unrecognized attribute '%s'", attrs[i]);
++i;
@@ -981,7 +1004,7 @@
return updateMediaCodec(
a_rank, parseCommaSeparatedStringSet(a_domain),
- parseCommaSeparatedStringSet(a_variant), a_enabled);
+ parseCommaSeparatedStringSet(a_variant), a_enabled, a_minsdk);
}
MediaCodecsXmlParser::Impl::Result
@@ -1035,7 +1058,7 @@
status_t MediaCodecsXmlParser::Impl::Parser::updateMediaCodec(
const char *rank, const StringSet &domains, const StringSet &variants,
- const char *enabled) {
+ const char *enabled, const char *minsdk) {
CHECK(mState->inCodec());
CodecProperties &codec = mState->codec();
@@ -1048,6 +1071,7 @@
codec.variantSet = variants;
+ // we allow sets of domains...
for (const std::string &domain : domains) {
if (domain.size() && domain.at(0) == '!') {
codec.domainSet.erase(domain.substr(1));
@@ -1065,6 +1089,49 @@
ALOGD("disabling %s", mState->codecName().c_str());
}
}
+
+ // evaluate against passed minsdk, with lots of logging to explain the logic
+ //
+ // if current sdk >= minsdk, we want to enable the codec
+ // this OVERRIDES any enabled="true|false" setting on the codec.
+ // (enabled=true minsdk=35 on a sdk 34 device results in a disabled codec)
+ //
+ // Although minsdk is not parsed before Android U, we can carry media_codecs.xml
+ // using this to devices earlier (e.g. as part of mainline). An example is appropriate.
+ //
+ // we have a codec that we want enabled in Android V (sdk=35), so we use:
+ // <MediaCodec ..... enabled="false" minsdk="35" >
+ //
+ // on Q/R/S/T: it sees enabled=false, but ignores the unrecognized minsdk
+ // so the codec will be disabled
+ // on U: it sees enabled=false, and sees minsdk=35, but U==34 and 34 < 35
+ // so the codec will be disabled
+ // on V: it sees enabled=false, and sees minsdk=35, V==35 and 35 >= 35
+ // so the codec will be enabled
+ //
+ // if we know the XML files will be used only on devices >= U, we can skip the enabled=false
+ // piece. Android mainline's support horizons say we will be using the enabled=false for
+ // another 4-5 years after U.
+ //
+ if (minsdk != nullptr) {
+ char *p = nullptr;
+ int sdk = strtol(minsdk, &p, 0);
+ if (p == minsdk || sdk < 0) {
+ ALOGE("minsdk parsing '%s' yielded %d, mapping to 0", minsdk, sdk);
+ sdk = 0;
+ }
+ // minsdk="#" means: "enable if sdk is >= #, disable otherwise"
+ if (mysdk < sdk) {
+ ALOGI("codec %s disabled, device sdk %d < required %d",
+ mState->codecName().c_str(), mysdk, sdk);
+ codec.quirkSet.emplace("attribute::disabled");
+ } else {
+ ALOGI("codec %s enabled, device sdk %d >= required %d",
+ mState->codecName().c_str(), mysdk, sdk);
+ codec.quirkSet.erase("attribute::disabled");
+ }
+ }
+
return OK;
}
diff --git a/media/libstagefright/xmlparser/api/current.txt b/media/libstagefright/xmlparser/api/current.txt
index ecfd85e..95c347a 100644
--- a/media/libstagefright/xmlparser/api/current.txt
+++ b/media/libstagefright/xmlparser/api/current.txt
@@ -84,6 +84,7 @@
method public java.util.List<media.codecs.Feature> getFeature_optional();
method public java.util.List<media.codecs.Limit> getLimit_optional();
method public java.util.List<media.codecs.Mapping> getMapping_optional();
+ method public String getMinsdk();
method public String getName();
method public java.util.List<media.codecs.Quirk> getQuirk_optional();
method public String getRank();
@@ -95,6 +96,7 @@
method public java.util.List<media.codecs.Variant> getVariant_optional();
method public void setDomain(String);
method public void setEnabled(String);
+ method public void setMinsdk(String);
method public void setName(String);
method public void setRank(String);
method public void setType(String);
diff --git a/media/libstagefright/xmlparser/media_codecs.xsd b/media/libstagefright/xmlparser/media_codecs.xsd
index c9a7efc..33f3a27 100644
--- a/media/libstagefright/xmlparser/media_codecs.xsd
+++ b/media/libstagefright/xmlparser/media_codecs.xsd
@@ -74,6 +74,7 @@
<xs:attribute name="domain" type="xs:string"/>
<xs:attribute name="variant" type="xs:string"/>
<xs:attribute name="enabled" type="xs:string"/>
+ <xs:attribute name="minsdk" type="xs:string"/>
</xs:complexType>
<xs:complexType name="Quirk">
<xs:attribute name="name" type="xs:string"/>
diff --git a/media/libstagefright/xmlparser/test/XMLParserTest.cpp b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
index 7629d97..2c5821e 100644
--- a/media/libstagefright/xmlparser/test/XMLParserTest.cpp
+++ b/media/libstagefright/xmlparser/test/XMLParserTest.cpp
@@ -145,6 +145,33 @@
},
{}, "");
+ // minsdk
+ setCodecProperties("test12.encoder", true, 12, {"attribute::disabled"}, {}, {}, "video/t12",
+ {
+ pair<string, string>("tuning-enable-goal", "no"),
+ },
+ {}, "");
+ setCodecProperties("test13.encoder", true, 13, {"attribute::disabled"}, {}, {}, "video/t13",
+ {
+ pair<string, string>("tuning-enable-goal", "no"),
+ },
+ {}, "");
+ setCodecProperties("test14.encoder", true, 14, {"attribute::disabled"}, {}, {}, "video/t14",
+ {
+ pair<string, string>("tuning-enable-goal", "no"),
+ },
+ {}, "");
+ setCodecProperties("test15.encoder", true, 15, {}, {}, {}, "video/t15",
+ {
+ pair<string, string>("tuning-enable-goal", "yes"),
+ },
+ {}, "");
+ setCodecProperties("test16.encoder", true, 16, {}, {}, {}, "video/t16",
+ {
+ pair<string, string>("tuning-enable-goal", "yes"),
+ },
+ {}, "");
+
setRoleProperties("audio_decoder.mp3", false, 1, "audio/mpeg", "test1.decoder",
{pair<string, string>("attribute::disabled", "present"),
pair<string, string>("rank", "4")});
@@ -191,6 +218,22 @@
pair<string, string>("tuning-pi", "3.1415")
});
+ // minsdk
+ setRoleProperties("video_encoder.t12", true, 12, "video/t12", "test12.encoder",
+ {pair<string, string>("tuning-enable-goal", "no"),
+ pair<string, string>("attribute::disabled", "present") });
+ setRoleProperties("video_encoder.t13", true, 13, "video/t13", "test13.encoder",
+ {pair<string, string>("tuning-enable-goal", "no"),
+ pair<string, string>("attribute::disabled", "present") });
+ setRoleProperties("video_encoder.t14", true, 14, "video/t14", "test14.encoder",
+ {pair<string, string>("tuning-enable-goal", "no"),
+ pair<string, string>("attribute::disabled", "present") });
+ setRoleProperties("video_encoder.t15", true, 15, "video/t15", "test15.encoder",
+ {pair<string, string>("tuning-enable-goal", "yes")});
+ setRoleProperties("video_encoder.t16", true, 16, "video/t16", "test16.encoder",
+ {pair<string, string>("tuning-enable-goal", "yes")});
+
+
setServiceAttribute(
{pair<string, string>("domain-telephony", "0"), pair<string, string>("domain-tv", "0"),
pair<string, string>("setting2", "0"), pair<string, string>("variant-variant1", "0")});
diff --git a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
index 8cae423..e066927 100644
--- a/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
+++ b/media/libstagefright/xmlparser/test/testdata/media_codecs_unit_test.xml
@@ -88,5 +88,21 @@
<Tuning name="hungry" value="yes"/>
<Tuning name="pi" value="3.1415"/>
</MediaCodec>
+ <!-- test minsdk -->
+ <MediaCodec name="test12.encoder" type="video/t12" minsdk="100">
+ <Tuning name="enable-goal" value="no"/>
+ </MediaCodec>
+ <MediaCodec name="test13.encoder" type="video/t13" enabled="false" minsdk="100">
+ <Tuning name="enable-goal" value="no"/>
+ </MediaCodec>
+ <MediaCodec name="test14.encoder" type="video/t14" enabled="true" minsdk="100">
+ <Tuning name="enable-goal" value="no"/>
+ </MediaCodec>
+ <MediaCodec name="test15.encoder" type="video/t15" minsdk="34">
+ <Tuning name="enable-goal" value="yes"/>
+ </MediaCodec>
+ <MediaCodec name="test16.encoder" type="video/t16" enabled="false" minsdk="34">
+ <Tuning name="enable-goal" value="yes"/>
+ </MediaCodec>
</Encoders>
</Included>
diff --git a/media/ndk/fuzzer/Android.bp b/media/ndk/fuzzer/Android.bp
index a3d6a96..999bac8 100644
--- a/media/ndk/fuzzer/Android.bp
+++ b/media/ndk/fuzzer/Android.bp
@@ -116,3 +116,16 @@
header_libs: ["libnativewindow_headers",],
defaults: ["libmediandk_fuzzer_defaults",],
}
+
+cc_fuzz {
+ name: "ndk_async_codec_fuzzer",
+ srcs: [
+ "ndk_async_codec_fuzzer.cpp",
+ "NdkMediaCodecFuzzerBase.cpp",
+ ],
+ header_libs: [
+ "libnativewindow_headers",
+ "libutils_headers",
+ ],
+ defaults: ["libmediandk_fuzzer_defaults",],
+}
diff --git a/media/ndk/fuzzer/README.md b/media/ndk/fuzzer/README.md
index 0fd08b0..7f6bdd7 100644
--- a/media/ndk/fuzzer/README.md
+++ b/media/ndk/fuzzer/README.md
@@ -8,6 +8,7 @@
+ [ndk_drm_fuzzer](#NdkDrm)
+ [ndk_mediamuxer_fuzzer](#NdkMediaMuxer)
+ [ndk_sync_codec_fuzzer](#NdkSyncCodec)
++ [ndk_async_codec_fuzzer](#NdkAsyncCodec)
# <a name="NdkCrypto"></a> Fuzzer for NdkCrypto
@@ -156,3 +157,16 @@
$ adb sync data
$ adb shell /data/fuzz/arm64/ndk_sync_codec_fuzzer/ndk_sync_codec_fuzzer
```
+
+# <a name="NdkAsyncCodec"></a>Fuzzer for NdkAsyncCodec
+
+#### Steps to run
+1. Build the fuzzer
+```
+ $ mm -j$(nproc) ndk_async_codec_fuzzer
+```
+2. Run on device
+```
+ $ adb sync data
+ $ adb shell /data/fuzz/arm64/ndk_async_codec_fuzzer/ndk_sync_codec_fuzzer
+```
diff --git a/media/ndk/fuzzer/ndk_async_codec_fuzzer.cpp b/media/ndk/fuzzer/ndk_async_codec_fuzzer.cpp
new file mode 100644
index 0000000..60d271f
--- /dev/null
+++ b/media/ndk/fuzzer/ndk_async_codec_fuzzer.cpp
@@ -0,0 +1,379 @@
+/*
+ * Copyright (C) 2022 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 <NdkMediaCodecFuzzerBase.h>
+#include <media/NdkMediaFormatPriv.h>
+#include <mutex>
+#include <queue>
+#include <thread>
+
+using namespace android;
+using namespace std;
+
+constexpr int32_t kMaxCryptoInfoAPIs = 3;
+constexpr int32_t kMaxNdkCodecAPIs = 5;
+
+template <typename T>
+class CallBackQueue {
+ public:
+ void push(T elem) {
+ bool needsNotify = false;
+ {
+ unique_lock<mutex> lock(mMutex);
+ needsNotify = mQueue.empty();
+ mQueue.push(std::move(elem));
+ }
+ if (needsNotify) {
+ mQueueNotEmptyCondition.notify_one();
+ }
+ }
+
+ T pop() {
+ unique_lock<mutex> lock(mMutex);
+ if (mQueue.empty()) {
+ mQueueNotEmptyCondition.wait(lock, [this]() { return !mQueue.empty(); });
+ }
+ auto result = mQueue.front();
+ mQueue.pop();
+ return result;
+ }
+
+ private:
+ mutex mMutex;
+ std::queue<T> mQueue;
+ std::condition_variable mQueueNotEmptyCondition;
+};
+
+class CallBackHandle {
+ public:
+ CallBackHandle() : mSawError(false), mIsDone(false) {}
+
+ virtual ~CallBackHandle() {
+ if (mIOThread.joinable()) {
+ mIOThread.join();
+ }
+ }
+
+ void ioThread();
+
+ // Implementation in child class (Decoder/Encoder)
+ virtual void invokeInputBufferAPI(AMediaCodec* codec, int32_t index) {
+ (void)codec;
+ (void)index;
+ }
+ virtual void onFormatChanged(AMediaCodec* codec, AMediaFormat* format) {
+ (void)codec;
+ (void)format;
+ }
+ virtual void receiveError(void) {}
+ virtual void invokeOutputBufferAPI(AMediaCodec* codec, int32_t index,
+ AMediaCodecBufferInfo* bufferInfo) {
+ (void)codec;
+ (void)index;
+ (void)bufferInfo;
+ }
+
+ // Keep a queue of all function callbacks.
+ typedef function<void()> IOTask;
+ CallBackQueue<IOTask> mIOQueue;
+ thread mIOThread;
+ bool mSawError;
+ bool mIsDone;
+};
+
+void CallBackHandle::ioThread() {
+ while (!mIsDone && !mSawError) {
+ auto task = mIOQueue.pop();
+ task();
+ }
+}
+
+static void onAsyncInputAvailable(AMediaCodec* codec, void* userdata, int32_t index) {
+ CallBackHandle* self = (CallBackHandle*)userdata;
+ self->mIOQueue.push([self, codec, index]() { self->invokeInputBufferAPI(codec, index); });
+}
+
+static void onAsyncOutputAvailable(AMediaCodec* codec, void* userdata, int32_t index,
+ AMediaCodecBufferInfo* bufferInfo) {
+ CallBackHandle* self = (CallBackHandle*)userdata;
+ AMediaCodecBufferInfo bufferInfoCopy = *bufferInfo;
+ self->mIOQueue.push([self, codec, index, bufferInfoCopy]() {
+ AMediaCodecBufferInfo bc = bufferInfoCopy;
+ self->invokeOutputBufferAPI(codec, index, &bc);
+ });
+}
+
+static void onAsyncFormatChanged(AMediaCodec* codec, void* userdata, AMediaFormat* format) {
+ (void)codec;
+ (void)userdata;
+ (void)format;
+};
+
+static void onAsyncError(AMediaCodec* codec, void* userdata, media_status_t err, int32_t actionCode,
+ const char* detail) {
+ CallBackHandle* self = (CallBackHandle*)userdata;
+ self->mSawError = true;
+ self->receiveError();
+ (void)codec;
+ (void)err;
+ (void)actionCode;
+ (void)detail;
+};
+
+class NdkAsyncCodecFuzzer : public NdkMediaCodecFuzzerBase, public CallBackHandle {
+ public:
+ NdkAsyncCodecFuzzer(const uint8_t* data, size_t size)
+ : NdkMediaCodecFuzzerBase(), mFdp(data, size) {
+ setFdp(&mFdp);
+ mStopCodec = false;
+ mSawInputEOS = false;
+ mSignalledError = false;
+ mIsEncoder = false;
+ mNumOfFrames = 0;
+ mNumInputFrames = 0;
+ };
+ void process();
+
+ static void codecOnFrameRendered(AMediaCodec* codec, void* userdata, int64_t mediaTimeUs,
+ int64_t systemNano) {
+ (void)codec;
+ (void)userdata;
+ (void)mediaTimeUs;
+ (void)systemNano;
+ };
+
+ private:
+ FuzzedDataProvider mFdp;
+ AMediaCodec* mCodec = nullptr;
+ void invokeCodecCryptoInfoAPI();
+ void invokekAsyncCodecAPIs(bool isEncoder);
+ void invokeAsyncCodeConfigAPI();
+ void invokeInputBufferAPI(AMediaCodec* codec, int32_t bufferIndex);
+ void invokeOutputBufferAPI(AMediaCodec* codec, int32_t bufferIndex,
+ AMediaCodecBufferInfo* bufferInfo);
+ void invokeFormatAPI(AMediaCodec* codec);
+ void receiveError();
+ bool mStopCodec;
+ bool mSawInputEOS;
+ bool mSignalledError;
+ int32_t mNumOfFrames;
+ int32_t mNumInputFrames;
+ mutable Mutex mMutex;
+ bool mIsEncoder;
+};
+
+void NdkAsyncCodecFuzzer::receiveError(void) {
+ mSignalledError = true;
+}
+
+void NdkAsyncCodecFuzzer::invokeInputBufferAPI(AMediaCodec* codec, int32_t bufferIndex) {
+ size_t bufferSize = 0;
+ Mutex::Autolock autoLock(mMutex);
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ return;
+ }
+ if (mStopCodec || bufferIndex < 0 || mSawInputEOS) {
+ return;
+ }
+
+ uint8_t* buffer = AMediaCodec_getInputBuffer(codec, bufferIndex, &bufferSize);
+ if (buffer) {
+ std::vector<uint8_t> bytesRead = mFdp.ConsumeBytes<uint8_t>(
+ std::min(mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes), bufferSize));
+ memcpy(buffer, bytesRead.data(), bytesRead.size());
+ bufferSize = bytesRead.size();
+ } else {
+ mSignalledError = true;
+ return;
+ }
+
+ uint32_t flag = 0;
+ if (!bufferSize || mNumInputFrames == mNumOfFrames) {
+ flag |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
+ mSawInputEOS = true;
+ }
+ AMediaCodec_queueInputBuffer(codec, bufferIndex, 0 /* offset */, bufferSize, 0 /* time */,
+ flag);
+ mNumInputFrames++;
+}
+
+void NdkAsyncCodecFuzzer::invokeOutputBufferAPI(AMediaCodec* codec, int32_t bufferIndex,
+ AMediaCodecBufferInfo* bufferInfo) {
+ size_t bufferSize = 0;
+ Mutex::Autolock autoLock(mMutex);
+
+ if (mSignalledError) {
+ CallBackHandle::mSawError = true;
+ return;
+ }
+
+ if (mStopCodec || bufferIndex < 0 || mIsDone) {
+ return;
+ }
+
+ if (!mIsEncoder) {
+ (void)AMediaCodec_getOutputBuffer(codec, bufferIndex, &bufferSize);
+ }
+ AMediaCodec_releaseOutputBuffer(codec, bufferIndex, mFdp.ConsumeBool());
+ mIsDone = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
+}
+
+void NdkAsyncCodecFuzzer::invokeFormatAPI(AMediaCodec* codec) {
+ AMediaFormat* codecFormat = nullptr;
+ if (mFdp.ConsumeBool()) {
+ codecFormat = AMediaCodec_getInputFormat(codec);
+ } else {
+ codecFormat = AMediaCodec_getOutputFormat(codec);
+ }
+ if (codecFormat) {
+ AMediaFormat_delete(codecFormat);
+ }
+}
+
+void NdkAsyncCodecFuzzer::invokekAsyncCodecAPIs(bool isEncoder) {
+ ANativeWindow* nativeWindow = nullptr;
+
+ if (mFdp.ConsumeBool()) {
+ AMediaCodec_createInputSurface(mCodec, &nativeWindow);
+ }
+
+ if (AMEDIA_OK == AMediaCodec_configure(mCodec, getCodecFormat(), nativeWindow,
+ nullptr /* crypto */,
+ (isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0))) {
+ mNumOfFrames = mFdp.ConsumeIntegralInRange<size_t>(kMinIterations, kMaxIterations);
+ // Configure codecs to run in async mode.
+ AMediaCodecOnAsyncNotifyCallback callBack = {onAsyncInputAvailable, onAsyncOutputAvailable,
+ onAsyncFormatChanged, onAsyncError};
+ AMediaCodec_setAsyncNotifyCallback(mCodec, callBack, this);
+ mIOThread = thread(&CallBackHandle::ioThread, this);
+
+ AMediaCodec_start(mCodec);
+ sleep(5);
+ int32_t count = 0;
+ while (++count <= mNumOfFrames) {
+ int32_t ndkcodecAPI =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxNdkCodecAPIs);
+ switch (ndkcodecAPI) {
+ case 0: { // get input and output Format
+ invokeFormatAPI(mCodec);
+ break;
+ }
+ case 1: {
+ AMediaCodec_signalEndOfInputStream(mCodec);
+ mSawInputEOS = true;
+ break;
+ }
+ case 2: { // set parameters
+ // Create a new parameter and set
+ AMediaFormat* params = AMediaFormat_new();
+ AMediaFormat_setInt32(
+ params, "video-bitrate",
+ mFdp.ConsumeIntegralInRange<size_t>(kMinIntKeyValue, kMaxIntKeyValue));
+ AMediaCodec_setParameters(mCodec, params);
+ AMediaFormat_delete(params);
+ break;
+ }
+ case 3: { // flush codec
+ AMediaCodec_flush(mCodec);
+ if (mFdp.ConsumeBool()) {
+ AMediaCodec_start(mCodec);
+ }
+ break;
+ }
+ case 4: {
+ char* name = nullptr;
+ AMediaCodec_getName(mCodec, &name);
+ AMediaCodec_releaseName(mCodec, name);
+ break;
+ }
+ case 5:
+ default: {
+ std::vector<uint8_t> userData = mFdp.ConsumeBytes<uint8_t>(
+ mFdp.ConsumeIntegralInRange<size_t>(kMinBytes, kMaxBytes));
+ AMediaCodecOnFrameRendered callback = codecOnFrameRendered;
+ AMediaCodec_setOnFrameRenderedCallback(mCodec, callback, userData.data());
+ break;
+ }
+ }
+ }
+ {
+ Mutex::Autolock autoLock(mMutex);
+ mStopCodec = 1;
+ AMediaCodec_stop(mCodec);
+ }
+ }
+
+ if (nativeWindow) {
+ ANativeWindow_release(nativeWindow);
+ }
+}
+
+void NdkAsyncCodecFuzzer::invokeAsyncCodeConfigAPI() {
+ while (mFdp.remaining_bytes() > 0) {
+ mIsEncoder = mFdp.ConsumeBool();
+ mCodec = createCodec(mIsEncoder, mFdp.ConsumeBool() /* isCodecForClient */);
+ if (mCodec) {
+ invokekAsyncCodecAPIs(mIsEncoder);
+ AMediaCodec_delete(mCodec);
+ }
+ }
+}
+
+void NdkAsyncCodecFuzzer::invokeCodecCryptoInfoAPI() {
+ while (mFdp.remaining_bytes() > 0) {
+ AMediaCodecCryptoInfo* cryptoInfo = getAMediaCodecCryptoInfo();
+ int32_t ndkCryptoInfoAPI =
+ mFdp.ConsumeIntegralInRange<size_t>(kMinAPICase, kMaxCryptoInfoAPIs);
+ switch (ndkCryptoInfoAPI) {
+ case 0: {
+ size_t sizes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getEncryptedBytes(cryptoInfo, sizes);
+ break;
+ }
+ case 1: {
+ size_t sizes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getClearBytes(cryptoInfo, sizes);
+ break;
+ }
+ case 2: {
+ uint8_t bytes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getIV(cryptoInfo, bytes);
+ break;
+ }
+ case 3:
+ default: {
+ uint8_t bytes[kMaxCryptoKey];
+ AMediaCodecCryptoInfo_getKey(cryptoInfo, bytes);
+ break;
+ }
+ }
+ AMediaCodecCryptoInfo_delete(cryptoInfo);
+ }
+}
+
+void NdkAsyncCodecFuzzer::process() {
+ if (mFdp.ConsumeBool()) {
+ invokeCodecCryptoInfoAPI();
+ } else {
+ invokeAsyncCodeConfigAPI();
+ }
+}
+
+extern "C" int LLVMFuzzerTestOneInput(const uint8_t* data, size_t size) {
+ NdkAsyncCodecFuzzer ndkAsyncCodecFuzzer(data, size);
+ ndkAsyncCodecFuzzer.process();
+ return 0;
+}
diff --git a/media/utils/tests/Android.bp b/media/utils/tests/Android.bp
index 0689083..3fdc6eb 100644
--- a/media/utils/tests/Android.bp
+++ b/media/utils/tests/Android.bp
@@ -200,7 +200,10 @@
name: "timerthread_tests",
defaults: ["libmediautils_tests_defaults"],
-
+ // TODO(b/270180838)
+ test_options: {
+ unit_test: false,
+ },
srcs: [
"TimerThread-test.cpp",
],