Merge "Revert "libaudiohal@aidl: Make IHalAdapterVendorExtension mandatory"" into main
diff --git a/media/aconfig/codec_fwk.aconfig b/media/aconfig/codec_fwk.aconfig
index 183bd99..06aceeb 100644
--- a/media/aconfig/codec_fwk.aconfig
+++ b/media/aconfig/codec_fwk.aconfig
@@ -5,6 +5,13 @@
package: "android.media.codec"
flag {
+ name: "aidl_hal_input_surface"
+ namespace: "codec_fwk"
+ description: "Feature flags for enabling AIDL HAL InputSurface handling"
+ bug: "201479783"
+}
+
+flag {
name: "dynamic_color_aspects"
namespace: "codec_fwk"
description: "Feature flag for dynamic color aspect support"
diff --git a/media/codec2/Android.mk b/media/codec2/Android.mk
deleted file mode 100644
index 82d739f..0000000
--- a/media/codec2/Android.mk
+++ /dev/null
@@ -1,48 +0,0 @@
-# =============================================================================
-# DOCUMENTATION GENERATION
-# =============================================================================
-C2_ROOT := $(call my-dir)
-
-C2_DOCS_ROOT := $(OUT_DIR)/target/common/docs/codec2
-
-C2_OUT_TEMP := $(PRODUCT_OUT)/gen/ETC/Codec2-docs_intermediates
-
-C2_DOXY := $(or $(shell command -v doxygen),\
- $(shell command -v /Applications/Doxygen.app/Contents/Resources/doxygen))
-
-.PHONY: check-doxygen
-check-doxygen:
-ifndef C2_DOXY
- $(error 'doxygen is not available')
-endif
-
-$(C2_OUT_TEMP)/doxy-api.config: $(C2_ROOT)/docs/doxygen.config
- # only document include directory, no internal sections
- sed 's/\(^INPUT *=.*\)/\1include\//; \
- s/\(^INTERNAL_DOCS *= *\).*/\1NO/; \
- s/\(^ENABLED_SECTIONS *=.*\)INTERNAL\(.*\).*/\1\2/; \
- s:\(^OUTPUT_DIRECTORY *= \)out:\1'$(OUT_DIR)':;' \
- $(C2_ROOT)/docs/doxygen.config > $@
-
-$(C2_OUT_TEMP)/doxy-internal.config: $(C2_ROOT)/docs/doxygen.config
- sed 's:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$(OUT_DIR)'\2internal:;' \
- $(C2_ROOT)/docs/doxygen.config > $@
-
-.PHONY: docs-api
-docs-api: $(C2_OUT_TEMP)/doxy-api.config check-doxygen
- echo API docs are building in $(C2_DOCS_ROOT)/api
- rm -rf $(C2_DOCS_ROOT)/api
- mkdir -p $(C2_DOCS_ROOT)/api
- $(C2_DOXY) $(C2_OUT_TEMP)/doxy-api.config
-
-.PHONY: docs-internal
-docs-internal: $(C2_OUT_TEMP)/doxy-internal.config check-doxygen
- echo Internal docs are building in $(C2_DOCS_ROOT)/internal
- rm -rf $(C2_DOCS_ROOT)/internal
- mkdir -p $(C2_DOCS_ROOT)/internal
- $(C2_DOXY) $(C2_OUT_TEMP)/doxy-internal.config
-
-.PHONY: docs-all
-docs-all: docs-api docs-internal
-
-include $(call all-makefiles-under,$(call my-dir))
diff --git a/media/codec2/components/OWNERS b/media/codec2/components/OWNERS
new file mode 100644
index 0000000..453999a
--- /dev/null
+++ b/media/codec2/components/OWNERS
@@ -0,0 +1 @@
+kyslov@google.com
\ No newline at end of file
diff --git a/media/codec2/components/aac/C2SoftAacDec.cpp b/media/codec2/components/aac/C2SoftAacDec.cpp
index d1b08bd..c770d0c 100644
--- a/media/codec2/components/aac/C2SoftAacDec.cpp
+++ b/media/codec2/components/aac/C2SoftAacDec.cpp
@@ -35,14 +35,14 @@
#define FILEREAD_MAX_LAYERS 2
-#define DRC_DEFAULT_MOBILE_REF_LEVEL -16.0 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
-#define DRC_DEFAULT_MOBILE_DRC_CUT 1.0 /* maximum compression of dynamic range for mobile conf */
-#define DRC_DEFAULT_MOBILE_DRC_BOOST 1.0 /* maximum compression of dynamic range for mobile conf */
-#define DRC_DEFAULT_MOBILE_DRC_HEAVY C2Config::DRC_COMPRESSION_HEAVY /* switch for heavy compression for mobile conf */
+#define DRC_DEFAULT_MOBILE_REF_LEVEL 64 /* 64*-0.25dB = -16 dB below full scale for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_CUT 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_BOOST 127 /* maximum compression of dynamic range for mobile conf */
+#define DRC_DEFAULT_MOBILE_DRC_HEAVY 1 /* switch for heavy compression for mobile conf */
#define DRC_DEFAULT_MOBILE_DRC_EFFECT 3 /* MPEG-D DRC effect type; 3 => Limited playback range */
#define DRC_DEFAULT_MOBILE_DRC_ALBUM 0 /* MPEG-D DRC album mode; 0 => album mode is disabled, 1 => album mode is enabled */
#define DRC_DEFAULT_MOBILE_OUTPUT_LOUDNESS (0.25) /* decoder output loudness; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
-#define DRC_DEFAULT_MOBILE_ENC_LEVEL (0.25) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
+#define DRC_DEFAULT_MOBILE_ENC_LEVEL (-1) /* encoder target level; -1 => the value is unknown, otherwise dB step value (e.g. 64 for -16 dB) */
#define MAX_CHANNEL_COUNT 8 /* maximum number of audio channels that can be decoded */
// names of properties that can be used to override the default DRC settings
#define PROP_DRC_OVERRIDE_REF_LEVEL "aac_drc_reference_level"
@@ -145,9 +145,13 @@
.withSetter(ProfileLevelSetter)
.build());
+ C2Config::drc_compression_mode_t defaultCompressionMode =
+ property_get_int32(PROP_DRC_OVERRIDE_HEAVY, DRC_DEFAULT_MOBILE_DRC_HEAVY) == 1
+ ? C2Config::DRC_COMPRESSION_HEAVY
+ : C2Config::DRC_COMPRESSION_LIGHT;
addParameter(
DefineParam(mDrcCompressMode, C2_PARAMKEY_DRC_COMPRESSION_MODE)
- .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, C2Config::DRC_COMPRESSION_HEAVY))
+ .withDefault(new C2StreamDrcCompressionModeTuning::input(0u, defaultCompressionMode))
.withFields({
C2F(mDrcCompressMode, value).oneOf({
C2Config::DRC_COMPRESSION_ODM_DEFAULT,
@@ -158,37 +162,48 @@
.withSetter(Setter<decltype(*mDrcCompressMode)>::StrictValueWithNoDeps)
.build());
+
+ float defaultRefLevel = -0.25 * property_get_int32(PROP_DRC_OVERRIDE_REF_LEVEL,
+ DRC_DEFAULT_MOBILE_REF_LEVEL);
addParameter(
DefineParam(mDrcTargetRefLevel, C2_PARAMKEY_DRC_TARGET_REFERENCE_LEVEL)
- .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, DRC_DEFAULT_MOBILE_REF_LEVEL))
+ .withDefault(new C2StreamDrcTargetReferenceLevelTuning::input(0u, defaultRefLevel))
.withFields({C2F(mDrcTargetRefLevel, value).inRange(-31.75, 0.25)})
.withSetter(Setter<decltype(*mDrcTargetRefLevel)>::StrictValueWithNoDeps)
.build());
+ float defaultEncLevel = -0.25 * property_get_int32(PROP_DRC_OVERRIDE_ENC_LEVEL,
+ DRC_DEFAULT_MOBILE_ENC_LEVEL);
addParameter(
DefineParam(mDrcEncTargetLevel, C2_PARAMKEY_DRC_ENCODED_TARGET_LEVEL)
- .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, DRC_DEFAULT_MOBILE_ENC_LEVEL))
+ .withDefault(new C2StreamDrcEncodedTargetLevelTuning::input(0u, defaultEncLevel))
.withFields({C2F(mDrcEncTargetLevel, value).inRange(-31.75, 0.25)})
.withSetter(Setter<decltype(*mDrcEncTargetLevel)>::StrictValueWithNoDeps)
.build());
+ float defaultDrcBoost =
+ property_get_int32(PROP_DRC_OVERRIDE_BOOST, DRC_DEFAULT_MOBILE_DRC_BOOST) / 127.;
addParameter(
DefineParam(mDrcBoostFactor, C2_PARAMKEY_DRC_BOOST_FACTOR)
- .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_BOOST))
+ .withDefault(new C2StreamDrcBoostFactorTuning::input(0u, defaultDrcBoost))
.withFields({C2F(mDrcBoostFactor, value).inRange(0, 1.)})
.withSetter(Setter<decltype(*mDrcBoostFactor)>::StrictValueWithNoDeps)
.build());
+ float defaultDrcCut =
+ property_get_int32(PROP_DRC_OVERRIDE_CUT, DRC_DEFAULT_MOBILE_DRC_CUT) / 127.;
addParameter(
DefineParam(mDrcAttenuationFactor, C2_PARAMKEY_DRC_ATTENUATION_FACTOR)
- .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, DRC_DEFAULT_MOBILE_DRC_CUT))
+ .withDefault(new C2StreamDrcAttenuationFactorTuning::input(0u, defaultDrcCut))
.withFields({C2F(mDrcAttenuationFactor, value).inRange(0, 1.)})
.withSetter(Setter<decltype(*mDrcAttenuationFactor)>::StrictValueWithNoDeps)
.build());
+ C2Config::drc_effect_type_t defaultDrcEffectType = (C2Config::drc_effect_type_t)
+ property_get_int32(PROP_DRC_OVERRIDE_EFFECT, DRC_DEFAULT_MOBILE_DRC_EFFECT);
addParameter(
DefineParam(mDrcEffectType, C2_PARAMKEY_DRC_EFFECT_TYPE)
- .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, C2Config::DRC_EFFECT_LIMITED_PLAYBACK_RANGE))
+ .withDefault(new C2StreamDrcEffectTypeTuning::input(0u, defaultDrcEffectType))
.withFields({
C2F(mDrcEffectType, value).oneOf({
C2Config::DRC_EFFECT_ODM_DEFAULT,
diff --git a/media/codec2/components/avc/C2SoftAvcDec.cpp b/media/codec2/components/avc/C2SoftAvcDec.cpp
index 96a4c4a..3385b95 100644
--- a/media/codec2/components/avc/C2SoftAvcDec.cpp
+++ b/media/codec2/components/avc/C2SoftAvcDec.cpp
@@ -416,6 +416,7 @@
ivdext_create_op_t s_create_op = {};
s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+ s_create_ip.u4_keep_threads_active = 1;
s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorFormat;
diff --git a/media/codec2/components/hevc/C2SoftHevcDec.cpp b/media/codec2/components/hevc/C2SoftHevcDec.cpp
index 15d6dcd..81db2a1 100644
--- a/media/codec2/components/hevc/C2SoftHevcDec.cpp
+++ b/media/codec2/components/hevc/C2SoftHevcDec.cpp
@@ -407,6 +407,7 @@
ivdext_create_op_t s_create_op = {};
s_create_ip.s_ivd_create_ip_t.u4_size = sizeof(ivdext_create_ip_t);
+ s_create_ip.u4_keep_threads_active = 1;
s_create_ip.s_ivd_create_ip_t.e_cmd = IVD_CMD_CREATE;
s_create_ip.s_ivd_create_ip_t.u4_share_disp_buf = 0;
s_create_ip.s_ivd_create_ip_t.e_output_format = mIvColorformat;
diff --git a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
index 439323c..491098d 100644
--- a/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
+++ b/media/codec2/components/mpeg2/C2SoftMpeg2Dec.cpp
@@ -433,6 +433,7 @@
s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.u4_size = sizeof(ivdext_fill_mem_rec_ip_t);
s_fill_mem_ip.u4_share_disp_buf = 0;
+ s_fill_mem_ip.u4_keep_threads_active = 1;
s_fill_mem_ip.e_output_format = mIvColorformat;
s_fill_mem_ip.u4_deinterlace = 1;
s_fill_mem_ip.s_ivd_fill_mem_rec_ip_t.e_cmd = IV_CMD_FILL_NUM_MEM_REC;
@@ -474,6 +475,7 @@
s_init_ip.s_ivd_init_ip_t.u4_frm_max_ht = mHeight;
s_init_ip.u4_share_disp_buf = 0;
s_init_ip.u4_deinterlace = 1;
+ s_init_ip.u4_keep_threads_active = 1;
s_init_ip.s_ivd_init_ip_t.u4_num_mem_rec = mNumMemRecords;
s_init_ip.s_ivd_init_ip_t.e_output_format = mIvColorformat;
s_init_op.s_ivd_init_op_t.u4_size = sizeof(ivdext_init_op_t);
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index e903069..2a33048 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -29,6 +29,12 @@
#define INT32_MAX 2147483647
#endif
+/* Quantization param values defined by the spec */
+#define VPX_QP_MIN 0
+#define VPX_QP_MAX 63
+#define VPX_QP_DEFAULT_MIN VPX_QP_MIN
+#define VPX_QP_DEFAULT_MAX VPX_QP_MAX
+
namespace android {
C2SoftVpxEnc::IntfImpl::IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
@@ -197,6 +203,20 @@
})
.withSetter(CodedColorAspectsSetter, mColorAspects)
.build());
+
+ addParameter(
+ DefineParam(mPictureQuantization, C2_PARAMKEY_PICTURE_QUANTIZATION)
+ .withDefault(C2StreamPictureQuantizationTuning::output::AllocShared(
+ 0 /* flexCount */, 0u /* stream */))
+ .withFields({C2F(mPictureQuantization, m.values[0].type_).oneOf(
+ {C2Config::I_FRAME, C2Config::P_FRAME}),
+ C2F(mPictureQuantization, m.values[0].min).inRange(
+ VPX_QP_DEFAULT_MIN, VPX_QP_DEFAULT_MAX),
+ C2F(mPictureQuantization, m.values[0].max).inRange(
+ VPX_QP_DEFAULT_MIN, VPX_QP_DEFAULT_MAX)})
+ .withSetter(PictureQuantizationSetter)
+ .build());
+
}
C2R C2SoftVpxEnc::IntfImpl::BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
@@ -330,6 +350,58 @@
double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
}
+
+C2R C2SoftVpxEnc::IntfImpl::PictureQuantizationSetter(bool mayBlock,
+ C2P<C2StreamPictureQuantizationTuning::output>
+ &me) {
+ (void)mayBlock;
+ // these are the ones we're going to set, so want them to default
+ // to the DEFAULT values for the codec
+ int32_t iMin = VPX_QP_DEFAULT_MIN, pMin = VPX_QP_DEFAULT_MIN;
+ int32_t iMax = VPX_QP_DEFAULT_MAX, pMax = VPX_QP_DEFAULT_MAX;
+ for (size_t i = 0; i < me.v.flexCount(); ++i) {
+ const C2PictureQuantizationStruct &layer = me.v.m.values[i];
+ // layerMin is clamped to [VPX_QP_MIN, layerMax] to avoid error
+ // cases where layer.min > layer.max
+ int32_t layerMax = std::clamp(layer.max, VPX_QP_MIN, VPX_QP_MAX);
+ int32_t layerMin = std::clamp(layer.min, VPX_QP_MIN, layerMax);
+ if (layer.type_ == C2Config::picture_type_t(I_FRAME)) {
+ iMax = layerMax;
+ iMin = layerMin;
+ ALOGV("iMin %d iMax %d", iMin, iMax);
+ } else if (layer.type_ == C2Config::picture_type_t(P_FRAME)) {
+ pMax = layerMax;
+ pMin = layerMin;
+ ALOGV("pMin %d pMax %d", pMin, pMax);
+ }
+ }
+ ALOGV("PictureQuantizationSetter(entry): i %d-%d p %d-%d",
+ iMin, iMax, pMin, pMax);
+
+ // vpx library takes same range for I/P picture type
+ int32_t maxFrameQP = std::min({iMax, pMax});
+ int32_t minFrameQP = std::max({iMin, pMin});
+ if (minFrameQP > maxFrameQP) {
+ minFrameQP = maxFrameQP;
+ }
+ // put them back into the structure
+ for (size_t i = 0; i < me.v.flexCount(); ++i) {
+ const C2PictureQuantizationStruct &layer = me.v.m.values[i];
+
+ if (layer.type_ == C2Config::picture_type_t(I_FRAME)) {
+ me.set().m.values[i].max = maxFrameQP;
+ me.set().m.values[i].min = minFrameQP;
+ }
+ else if (layer.type_ == C2Config::picture_type_t(P_FRAME)) {
+ me.set().m.values[i].max = maxFrameQP;
+ me.set().m.values[i].min = minFrameQP;
+ }
+ }
+ ALOGV("PictureQuantizationSetter(exit): minFrameQP = %d maxFrameQP = %d",
+ minFrameQP, maxFrameQP);
+ return C2R::Ok();
+}
+
C2R C2SoftVpxEnc::IntfImpl::ColorAspectsSetter(bool mayBlock,
C2P<C2StreamColorAspectsInfo::input>& me) {
(void)mayBlock;
@@ -453,6 +525,7 @@
mRequestSync = mIntf->getRequestSync_l();
mLayering = mIntf->getTemporalLayers_l();
mTemporalLayers = mLayering->m.layerCount;
+ mQpBounds = mIntf->getPictureQuantization_l();
}
switch (mBitrateMode->value) {
@@ -466,6 +539,18 @@
break;
}
+ if (mQpBounds->flexCount() > 0) {
+ // read min max qp for sequence
+ for (size_t i = 0; i < mQpBounds->flexCount(); ++i) {
+ const C2PictureQuantizationStruct &layer = mQpBounds->m.values[i];
+ if (layer.type_ == C2Config::picture_type_t(I_FRAME)) {
+ mMaxQuantizer = layer.max;
+ mMinQuantizer = layer.min;
+ break;
+ }
+ }
+ }
+
setCodecSpecificInterface();
if (!mCodecInterface) goto CleanUp;
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index bfb4444..980de04 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -219,6 +219,7 @@
std::shared_ptr<C2StreamBitrateModeTuning::output> mBitrateMode;
std::shared_ptr<C2StreamRequestSyncFrameTuning::output> mRequestSync;
std::shared_ptr<C2StreamTemporalLayeringTuning::output> mLayering;
+ std::shared_ptr<C2StreamPictureQuantizationTuning::output> mQpBounds;
C2_DO_NOT_COPY(C2SoftVpxEnc);
};
@@ -250,6 +251,9 @@
static C2R LayeringSetter(bool mayBlock, C2P<C2StreamTemporalLayeringTuning::output>& me);
+ static C2R PictureQuantizationSetter(bool mayBlock,
+ C2P<C2StreamPictureQuantizationTuning::output> &me);
+
// unsafe getters
std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const {
@@ -269,6 +273,9 @@
std::shared_ptr<C2StreamColorAspectsInfo::output> getCodedColorAspects_l() const {
return mCodedColorAspects;
}
+ std::shared_ptr<C2StreamPictureQuantizationTuning::output> getPictureQuantization_l() const {
+ return mPictureQuantization;
+ }
uint32_t getSyncFramePeriod() const;
static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me);
static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
@@ -287,6 +294,7 @@
std::shared_ptr<C2StreamProfileLevelInfo::output> mProfileLevel;
std::shared_ptr<C2StreamColorAspectsInfo::input> mColorAspects;
std::shared_ptr<C2StreamColorAspectsInfo::output> mCodedColorAspects;
+ std::shared_ptr<C2StreamPictureQuantizationTuning::output> mPictureQuantization;
};
} // namespace android
diff --git a/media/codec2/docs/doxygen.config b/media/codec2/docs/doxygen.config
index 5c3bea3..ab8b53b 100644
--- a/media/codec2/docs/doxygen.config
+++ b/media/codec2/docs/doxygen.config
@@ -162,7 +162,7 @@
# will be relative from the directory where doxygen is started.
# This tag requires that the tag FULL_PATH_NAMES is set to YES.
-STRIP_FROM_PATH = frameworks/av/media/libstagefright/codec2
+STRIP_FROM_PATH = frameworks/av/media/codec2
# The STRIP_FROM_INC_PATH tag can be used to strip a user-defined part of the
# path mentioned in the documentation of a class, which tells the reader which
@@ -781,7 +781,7 @@
# spaces. See also FILE_PATTERNS and EXTENSION_MAPPING
# Note: If this tag is empty the current directory is searched.
-INPUT = frameworks/av/media/libstagefright/codec2/
+INPUT = frameworks/av/media/codec2/
# This tag can be used to specify the character encoding of the source files
# that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses
@@ -897,7 +897,7 @@
# need to set EXTENSION_MAPPING for the extension otherwise the files are not
# properly processed by doxygen.
-INPUT_FILTER = frameworks/av/media/libstagefright/codec2/docs/doxyfilter.sh
+INPUT_FILTER = frameworks/av/media/codec2/docs/doxyfilter.sh
# The FILTER_PATTERNS tag can be used to specify filters on a per file pattern
# basis. Doxygen will compare the file name with each pattern and apply the
diff --git a/media/codec2/doxygen.sh b/media/codec2/doxygen.sh
new file mode 100755
index 0000000..ca5aeed
--- /dev/null
+++ b/media/codec2/doxygen.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+# Copyright (C) 2024 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.
+
+# =============================================================================
+# DOCUMENTATION GENERATION
+# =============================================================================
+
+if [ -z "$ANDROID_BUILD_TOP" ]; then
+ echo "error: Android build is not set up. Run this command after lunch." >&2
+ exit 2
+fi
+
+OUT_DIR=$ANDROID_BUILD_TOP/out
+
+# Codec 2.0 source and target paths
+C2_ROOT=$(dirname "$0")
+C2_DOCS_ROOT=$OUT_DIR/target/common/docs/codec2
+C2_OUT_TEMP=$ANDROID_PRODUCT_OUT/gen/ETC/Codec2-docs_intermediates
+
+# Doxygen path
+DOXY=$(which doxygen)
+DOXY_MAC="/Applications/Doxygen.app/Contents/Resources/doxygen"
+if [ -z "$DOXY" -a -x "$DOXY_MAC" ]; then
+ DOXY=$DOXY_MAC
+fi
+
+if [ -z "$DOXY" ]; then
+ echo "error: doxygen is not available" >&2
+ exit 2
+fi
+
+# Create doxygen config
+# ---------------------
+gen_doxy() {
+ local variant=$1
+ local variant_lc=$(echo $variant | tr A-Z a-z)
+ mkdir -p $C2_OUT_TEMP
+ if [ "$variant_lc" == "api" ]; then
+ # only document include directory, no internal sections
+ sed 's/\(^INPUT *=.*\)/\1core\/include\//;
+ s/\(^INTERNAL_DOCS *= *\).*/\1NO/;
+ s/\(^ENABLED_SECTIONS *=.*\)INTERNAL\(.*\).*/\1\2/;
+ s:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$OUT_DIR'\2'$variant_lc':;' \
+ $C2_ROOT/docs/doxygen.config > $C2_OUT_TEMP/doxy-$variant_lc.config
+
+ ls -la $C2_OUT_TEMP/doxy-$variant_lc.config
+ else
+ sed 's:\(^OUTPUT_DIRECTORY *= \)out\(.*\)api:\1'$OUT_DIR'\2'$variant_lc':;' \
+ $C2_ROOT/docs/doxygen.config > $C2_OUT_TEMP/doxy-$variant_lc.config
+ fi
+
+ echo $variant docs are building in $C2_DOCS_ROOT/$variant_lc
+ rm -rf $C2_DOCS_ROOT/$variant_lc
+ mkdir -p $C2_DOCS_ROOT/$variant_lc
+ pushd $ANDROID_BUILD_TOP
+ $DOXY $C2_OUT_TEMP/doxy-$variant_lc.config
+ popd
+}
+
+usage() {
+ echo "usage: $(basename "$0") [target]"
+ echo " where target can be one of:"
+ echo " all: build both API and internal docs (default)"
+ echo " api: build API docs only"
+ echo " internal: build internal docs which include implementation details"
+}
+
+TARGET=${1:-all}
+case "$TARGET" in
+ api) gen_doxy API;;
+ internal) gen_doxy Internal;;
+ all) gen_doxy API; gen_doxy Internal;;
+ -h) usage; exit 0;;
+ *) echo "unknown target '$TARGET'" >&2; usage; exit 2;;
+esac
diff --git a/media/codec2/hal/aidl/Component.cpp b/media/codec2/hal/aidl/Component.cpp
index bdc560f..ba5f8d4 100644
--- a/media/codec2/hal/aidl/Component.cpp
+++ b/media/codec2/hal/aidl/Component.cpp
@@ -46,13 +46,17 @@
using ::aidl::android::hardware::common::NativeHandle;
using ::aidl::android::hardware::media::bufferpool2::IClientManager;
using ::ndk::ScopedAStatus;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
Listener(const std::shared_ptr<Component>& component) :
- mComponent(component),
- mListener(component->mListener) {
+ mComponent(component),
+ mListener(component->mListener) {
+ CHECK(component);
+ CHECK(component->mListener);
}
virtual void onError_nb(
@@ -137,6 +141,52 @@
std::weak_ptr<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const std::shared_ptr<Component>& component,
+ const std::shared_ptr<MultiAccessUnitHelper> &helper):
+ Listener(component), mHelper(helper) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHelper->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHelper->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
// Component::DeathContext
struct Component::DeathContext {
std::weak_ptr<Component> mWeakComp;
@@ -149,14 +199,38 @@
const std::shared_ptr<ComponentStore>& store,
const std::shared_ptr<IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{SharedRefBase::make<ComponentInterface>(
- component->intf(), store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager},
mDeathContext(nullptr) {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ ::android::GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = SharedRefBase::make<ComponentInterface>(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -179,8 +253,21 @@
registerFrameData(mListener, work->input);
}
}
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ return ScopedAStatus::fromServiceSpecificError(err);
+ }
+ }
+ return ScopedAStatus::ok();
+ }
- c2_status_t err = mComponent->queue_nb(&c2works);
+ err = mComponent->queue_nb(&c2works);
if (err == C2_OK) {
return ScopedAStatus::ok();
}
@@ -192,7 +279,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
-
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
if (work) {
@@ -362,6 +451,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
if (status == C2_OK) {
return ScopedAStatus::ok();
@@ -375,6 +467,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
if (status == C2_OK) {
return ScopedAStatus::ok();
@@ -413,13 +508,20 @@
void Component::initListener(const std::shared_ptr<Component>& self) {
if (__builtin_available(android __ANDROID_API_T__, *)) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
mInit = res;
}
+ // b/321902635, mListener should not be null.
+ CHECK(mListener);
mDeathRecipient = ::ndk::ScopedAIBinder_DeathRecipient(
AIBinder_DeathRecipient_new(OnBinderDied));
mDeathContext = new DeathContext{ref<Component>()};
diff --git a/media/codec2/hal/aidl/ComponentInterface.cpp b/media/codec2/hal/aidl/ComponentInterface.cpp
index 2d812c9..1f0534d 100644
--- a/media/codec2/hal/aidl/ComponentInterface.cpp
+++ b/media/codec2/hal/aidl/ComponentInterface.cpp
@@ -24,6 +24,8 @@
#include <utils/Timers.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -43,9 +45,10 @@
// Implementation of ConfigurableC2Intf based on C2ComponentInterface
struct CompIntf : public ConfigurableC2Intf {
- CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+ CompIntf(const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf):
ConfigurableC2Intf{intf->getName(), intf->getId()},
- mIntf{intf} {
+ mIntf{intf}, mMultiAccessUnitIntf{multiAccessUnitIntf} {
}
virtual c2_status_t config(
@@ -53,7 +56,34 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2SettingResult>>* const failures
) override {
- return mIntf->config_vb(params, mayBlock, failures);
+ std::vector<C2Param*> paramsToIntf;
+ std::vector<C2Param*> paramsToLargeFrameIntf;
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->config_vb(params, mayBlock, failures);
+ return err;
+ }
+ for (auto &p : params) {
+ if (mMultiAccessUnitIntf->isParamSupported(p->index())) {
+ paramsToLargeFrameIntf.push_back(p);
+ } else {
+ paramsToIntf.push_back(p);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->config_vb(paramsToIntf, mayBlock, failures);
+ }
+ if (err1 != C2_OK) {
+ LOG(ERROR) << "We have a failed config";
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->config(
+ paramsToLargeFrameIntf, mayBlock, failures);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t query(
@@ -61,23 +91,56 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2Param>>* const params
) const override {
- return mIntf->query_vb({}, indices, mayBlock, params);
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->query_vb({}, indices, mayBlock, params);
+ return err;
+ }
+ std::vector<C2Param::Index> paramsToIntf;
+ std::vector<C2Param::Index> paramsToLargeFrameIntf;
+ for (auto &i : indices) {
+ if (mMultiAccessUnitIntf->isParamSupported(i)) {
+ paramsToLargeFrameIntf.push_back(i);
+ } else {
+ paramsToIntf.push_back(i);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->query_vb({}, paramsToIntf, mayBlock, params);
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->query(
+ {}, paramsToLargeFrameIntf, mayBlock, params);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t querySupportedParams(
std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
) const override {
- return mIntf->querySupportedParams_nb(params);
+ c2_status_t err = mIntf->querySupportedParams_nb(params);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedParams(params);
+ }
+ return err;
}
virtual c2_status_t querySupportedValues(
std::vector<C2FieldSupportedValuesQuery>& fields,
c2_blocking_t mayBlock) const override {
- return mIntf->querySupportedValues_vb(fields, mayBlock);
+ c2_status_t err = mIntf->querySupportedValues_vb(fields, mayBlock);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedValues(fields, mayBlock);
+ }
+ return err;
}
protected:
std::shared_ptr<C2ComponentInterface> mIntf;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
};
} // unnamed namespace
@@ -85,10 +148,16 @@
// ComponentInterface
ComponentInterface::ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<ParameterCache>& cache):ComponentInterface(intf, nullptr, cache) {
+}
+
+ComponentInterface::ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf,
const std::shared_ptr<ParameterCache>& cache)
: mInterface{intf},
mConfigurable{SharedRefBase::make<CachedConfigurable>(
- std::make_unique<CompIntf>(intf))} {
+ std::make_unique<CompIntf>(intf, multiAccessUnitIntf))} {
mInit = mConfigurable->init(cache);
}
diff --git a/media/codec2/hal/aidl/ComponentStore.cpp b/media/codec2/hal/aidl/ComponentStore.cpp
index f0a1490..ef49308 100644
--- a/media/codec2/hal/aidl/ComponentStore.cpp
+++ b/media/codec2/hal/aidl/ComponentStore.cpp
@@ -206,6 +206,15 @@
const std::shared_ptr<IClientManager>& pool,
std::shared_ptr<IComponent> *component) {
+ if (!listener) {
+ ALOGE("createComponent(): listener is null");
+ return ScopedAStatus::fromServiceSpecificError(Status::BAD_VALUE);
+ }
+ if (!pool) {
+ ALOGE("createComponent(): pool is null");
+ return ScopedAStatus::fromServiceSpecificError(Status::BAD_VALUE);
+ }
+
std::shared_ptr<C2Component> c2component;
c2_status_t status =
mStore->createComponent(name, &c2component);
@@ -218,7 +227,8 @@
std::shared_ptr<Component> comp =
SharedRefBase::make<Component>(c2component, listener, ref<ComponentStore>(), pool);
*component = comp;
- if (!component) {
+ if (!component || !comp) {
+ ALOGE("createComponent(): component cannot be returned");
status = C2_CORRUPTED;
} else {
reportComponentBirth(comp.get());
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/Component.h b/media/codec2/hal/aidl/include/codec2/aidl/Component.h
index 94b760f..9725bcf 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/Component.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/Component.h
@@ -31,6 +31,8 @@
#include <aidl/android/hardware/media/c2/IInputSurface.h>
#include <aidl/android/hardware/media/c2/IInputSurfaceConnection.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -46,6 +48,8 @@
namespace c2 {
namespace utils {
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -85,6 +89,8 @@
std::shared_ptr<C2Component> mComponent;
std::shared_ptr<ComponentInterface> mInterface;
std::shared_ptr<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
std::shared_ptr<ComponentStore> mStore;
DefaultBufferPoolSender mBufferPoolSender;
@@ -102,6 +108,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
::ndk::ScopedAIBinder_DeathRecipient mDeathRecipient;
static void OnBinderDied(void *cookie);
static void OnBinderUnlinked(void *cookie);
diff --git a/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h b/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
index 7723bee..bd19cd6 100644
--- a/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
+++ b/media/codec2/hal/aidl/include/codec2/aidl/ComponentInterface.h
@@ -22,11 +22,14 @@
#include <aidl/android/hardware/media/c2/BnComponentInterface.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
#include <memory>
+#include <set>
namespace aidl {
namespace android {
@@ -35,12 +38,16 @@
namespace c2 {
namespace utils {
-struct ComponentStore;
+using ::android::MultiAccessUnitInterface;
struct ComponentInterface : public BnComponentInterface {
ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& interface,
const std::shared_ptr<ParameterCache>& cache);
+ ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ const std::shared_ptr<MultiAccessUnitInterface>& largeBufferIntf,
+ const std::shared_ptr<ParameterCache>& cache);
c2_status_t status() const;
::ndk::ScopedAStatus getConfigurable(
std::shared_ptr<IConfigurable> *intf) override;
@@ -51,7 +58,6 @@
c2_status_t mInit;
};
-
} // namespace utils
} // namespace c2
} // namespace media
diff --git a/media/codec2/hal/common/Android.bp b/media/codec2/hal/common/Android.bp
index 2aedd8b..7d7b285 100644
--- a/media/codec2/hal/common/Android.bp
+++ b/media/codec2/hal/common/Android.bp
@@ -11,6 +11,7 @@
srcs: [
"BufferTypes.cpp",
+ "MultiAccessUnitHelper.cpp",
],
export_include_dirs: ["include/"],
@@ -26,7 +27,10 @@
"libcodec2_vndk",
"liblog",
"libstagefright_foundation",
+ "server_configurable_flags",
],
+
+ static_libs: ["aconfig_mediacodec_flags_c_lib"],
}
cc_library_static {
diff --git a/media/codec2/hal/common/MultiAccessUnitHelper.cpp b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
new file mode 100644
index 0000000..117ce91
--- /dev/null
+++ b/media/codec2/hal/common/MultiAccessUnitHelper.cpp
@@ -0,0 +1,742 @@
+/*
+ * Copyright 2023 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 "Codec2-MultiAccessUnitHelper"
+#include <android-base/logging.h>
+
+#include <com_android_media_codec_flags.h>
+
+#include <codec2/common/MultiAccessUnitHelper.h>
+#include <android-base/properties.h>
+
+#include <C2BufferPriv.h>
+#include <C2Debug.h>
+#include <C2PlatformSupport.h>
+
+namespace android {
+
+static C2R MultiAccessUnitParamsSetter(
+ bool mayBlock, C2InterfaceHelper::C2P<C2LargeFrame::output> &me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (!me.F(me.v.maxSize).supportsAtAll(me.v.maxSize)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.maxSize)));
+ } else if (!me.F(me.v.thresholdSize).supportsAtAll(me.v.thresholdSize)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.thresholdSize)));
+ } else if (me.v.maxSize < me.v.thresholdSize) {
+ me.set().maxSize = me.v.thresholdSize;
+ } else if (me.v.thresholdSize == 0 && me.v.maxSize > 0) {
+ me.set().thresholdSize = me.v.maxSize;
+ }
+ std::vector<std::unique_ptr<C2SettingResult>> failures;
+ res.retrieveFailures(&failures);
+ if (!failures.empty()) {
+ me.set().maxSize = 0;
+ me.set().thresholdSize = 0;
+ }
+ return res;
+}
+
+MultiAccessUnitInterface::MultiAccessUnitInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ std::shared_ptr<C2ReflectorHelper> helper)
+ : C2InterfaceHelper(helper), mC2ComponentIntf(interface) {
+ setDerivedInstance(this);
+ addParameter(
+ DefineParam(mLargeFrameParams, C2_PARAMKEY_OUTPUT_LARGE_FRAME)
+ .withDefault(new C2LargeFrame::output(0u, 0, 0))
+ .withFields({
+ C2F(mLargeFrameParams, maxSize).inRange(
+ 0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u)),
+ C2F(mLargeFrameParams, thresholdSize).inRange(
+ 0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u))
+ })
+ .withSetter(MultiAccessUnitParamsSetter)
+ .build());
+ std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams;
+ querySupportedParams(&supportedParams);
+ // Adding to set to do intf seperation in query/config
+ for (std::shared_ptr<C2ParamDescriptor> &desc : supportedParams) {
+ mSupportedParamIndexSet.insert(desc->index());
+ }
+
+ if (mC2ComponentIntf) {
+ c2_status_t err = mC2ComponentIntf->query_vb({&mKind}, {}, C2_MAY_BLOCK, nullptr);
+ }
+}
+
+bool MultiAccessUnitInterface::isParamSupported(C2Param::Index index) {
+ return (mSupportedParamIndexSet.count(index) != 0);
+}
+
+C2LargeFrame::output MultiAccessUnitInterface::getLargeFrameParam() const {
+ return *mLargeFrameParams;
+}
+
+C2Component::kind_t MultiAccessUnitInterface::kind() const {
+ return (C2Component::kind_t)(mKind.value);
+}
+
+void MultiAccessUnitInterface::getDecoderSampleRateAndChannelCount(
+ uint32_t &sampleRate_, uint32_t &channelCount_) const {
+ if (mC2ComponentIntf) {
+ C2StreamSampleRateInfo::output sampleRate;
+ C2StreamChannelCountInfo::output channelCount;
+ c2_status_t res = mC2ComponentIntf->query_vb(
+ {&sampleRate, &channelCount}, {}, C2_MAY_BLOCK, nullptr);
+ if (res == C2_OK) {
+ sampleRate_ = sampleRate.value;
+ channelCount_ = channelCount.value;
+ }
+ }
+}
+
+//C2MultiAccessUnitBuffer
+class C2MultiAccessUnitBuffer : public C2Buffer {
+ public:
+ explicit C2MultiAccessUnitBuffer(
+ const std::vector<C2ConstLinearBlock> &blocks):
+ C2Buffer(blocks) {
+ }
+};
+
+//MultiAccessUnitHelper
+MultiAccessUnitHelper::MultiAccessUnitHelper(
+ const std::shared_ptr<MultiAccessUnitInterface>& intf):
+ mInit(false),
+ mInterface(intf) {
+ std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore();
+ if(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator) == C2_OK) {
+ mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId);
+ mInit = true;
+ }
+}
+
+MultiAccessUnitHelper::~MultiAccessUnitHelper() {
+ std::unique_lock<std::mutex> l(mLock);
+ mFrameHolder.clear();
+}
+
+bool MultiAccessUnitHelper::isEnabledOnPlatform() {
+ bool result = com::android::media::codec::flags::provider_->large_audio_frame();
+ if (!result) {
+ false;
+ }
+ //TODO: remove this before launch
+ result = ::android::base::GetBoolProperty("debug.media.c2.large.audio.frame", true);
+ LOG(DEBUG) << "MultiAccessUnitHelper " << (result ? "enabled" : "disabled");
+ return result;
+}
+
+std::shared_ptr<MultiAccessUnitInterface> MultiAccessUnitHelper::getInterface() {
+ return mInterface;
+}
+
+bool MultiAccessUnitHelper::getStatus() {
+ return mInit;
+}
+
+void MultiAccessUnitHelper::reset() {
+ std::lock_guard<std::mutex> l(mLock);
+ mFrameHolder.clear();
+}
+
+c2_status_t MultiAccessUnitHelper::error(
+ std::list<std::unique_ptr<C2Work>> * const worklist) {
+ if (worklist == nullptr) {
+ LOG(ERROR) << "Provided null worklist for error()";
+ return C2_OK;
+ }
+ std::unique_lock<std::mutex> l(mLock);
+ for (auto frame = mFrameHolder.begin(); frame != mFrameHolder.end(); frame++) {
+ if (frame->mLargeWork) {
+ finalizeWork(*frame, 0, true);
+ worklist->push_back(std::move(frame->mLargeWork));
+ frame->reset();
+ }
+ }
+ mFrameHolder.clear();
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::flush(
+ std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) {
+ c2_status_t c2res = C2_OK;
+ std::lock_guard<std::mutex> l(mLock);
+ for (std::unique_ptr<C2Work>& w : *c2flushedWorks) {
+ bool foundFlushedFrame = false;
+ std::list<MultiAccessUnitInfo>::iterator frame =
+ mFrameHolder.begin();
+ while (frame != mFrameHolder.end() && !foundFlushedFrame) {
+ auto it = frame->mComponentFrameIds.find(
+ w->input.ordinal.frameIndex.peekull());
+ if (it != frame->mComponentFrameIds.end()) {
+ LOG(DEBUG) << "Multi access-unit flush"
+ << w->input.ordinal.frameIndex.peekull()
+ << " with " << frame->inOrdinal.frameIndex.peekull();
+ w->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
+ bool removeEntry = w->worklets.empty()
+ || !w->worklets.front()
+ || (w->worklets.front()->output.flags
+ & C2FrameData::FLAG_INCOMPLETE) == 0;
+ if (removeEntry) {
+ frame->mComponentFrameIds.erase(it);
+ }
+ foundFlushedFrame = true;
+ }
+ if (frame->mComponentFrameIds.empty()) {
+ frame = mFrameHolder.erase(frame);
+ } else {
+ ++frame;
+ }
+ }
+ }
+ return c2res;
+}
+
+c2_status_t MultiAccessUnitHelper::scatter(
+ std::list<std::unique_ptr<C2Work>> &largeWork,
+ std::list<std::list<std::unique_ptr<C2Work>>>* const processedWork) {
+ LOG(DEBUG) << "Multiple access-unit: scatter";
+ if (processedWork == nullptr) {
+ LOG(ERROR) << "MultiAccessUnitHelper provided with no work list";
+ return C2_CORRUPTED;
+ }
+ for (std::unique_ptr<C2Work>& w : largeWork) {
+ std::list<std::unique_ptr<C2Work>> sliceWork;
+ C2WorkOrdinalStruct inputOrdinal = w->input.ordinal;
+ // To hold correspondence and processing bits b/w input and output
+ MultiAccessUnitInfo frameInfo(inputOrdinal);
+ std::set<uint64_t>& frameSet = frameInfo.mComponentFrameIds;
+ uint64_t newFrameIdx = mFrameIndex++;
+ // TODO: Do not split buffers if component inherantly supports MultipleFrames.
+ // if thats case, only replace frameindex.
+ auto cloneInputWork = [&newFrameIdx](std::unique_ptr<C2Work>& inWork, uint32_t flags) {
+ std::unique_ptr<C2Work> newWork(new C2Work);
+ newWork->input.flags = (C2FrameData::flags_t)flags;
+ newWork->input.ordinal = inWork->input.ordinal;
+ newWork->input.ordinal.frameIndex = newFrameIdx;
+ if (!inWork->input.configUpdate.empty()) {
+ for (std::unique_ptr<C2Param>& param : inWork->input.configUpdate) {
+ newWork->input.configUpdate.push_back(
+ std::move(C2Param::Copy(*(param.get()))));
+ }
+ }
+ newWork->input.infoBuffers = (inWork->input.infoBuffers);
+ if (!inWork->worklets.empty() && inWork->worklets.front() != nullptr) {
+ newWork->worklets.emplace_back(new C2Worklet);
+ newWork->worklets.front()->component = inWork->worklets.front()->component;
+ std::vector<std::unique_ptr<C2Tuning>> tunings;
+ for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) {
+ tunings.push_back(std::move(
+ std::unique_ptr<C2Tuning>(
+ static_cast<C2Tuning*>(
+ C2Param::Copy(*(tuning.get())).release()))));
+ }
+ newWork->worklets.front()->tunings = std::move(tunings);
+ }
+ return newWork;
+ };
+ if (w->input.buffers.empty()
+ || (w->input.buffers.front() == nullptr)
+ || (!w->input.buffers.front()->hasInfo(
+ C2AccessUnitInfos::input::PARAM_TYPE))) {
+ LOG(DEBUG) << "Empty or MultiAU info buffer scatter frames with frameIndex "
+ << inputOrdinal.frameIndex.peekull()
+ << ") -> newFrameIndex " << newFrameIdx
+ <<" : input ts " << inputOrdinal.timestamp.peekull();
+ sliceWork.push_back(std::move(cloneInputWork(w, w->input.flags)));
+ if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) {
+ sliceWork.back()->input.buffers = std::move(w->input.buffers);
+ }
+ frameSet.insert(newFrameIdx);
+ processedWork->push_back(std::move(sliceWork));
+ } else {
+ const std::vector<std::shared_ptr<C2Buffer>>& inBuffers = w->input.buffers;
+ if (inBuffers.front()->data().linearBlocks().size() == 0) {
+ LOG(ERROR) << "ERROR: Work has Large frame info but has no linear blocks.";
+ return C2_CORRUPTED;
+ }
+ const std::vector<C2ConstLinearBlock>& multiAU =
+ inBuffers.front()->data().linearBlocks();
+ std::shared_ptr<const C2AccessUnitInfos::input> auInfo =
+ std::static_pointer_cast<const C2AccessUnitInfos::input>(
+ w->input.buffers.front()->getInfo(C2AccessUnitInfos::input::PARAM_TYPE));
+ uint32_t offset = 0; uint32_t multiAUSize = multiAU.front().size();
+ bool sendEos = false;
+ for (int idx = 0; idx < auInfo->flexCount(); ++idx) {
+ std::vector<C2ConstLinearBlock> au;
+ const C2AccessUnitInfosStruct &info = auInfo->m.values[idx];
+ sendEos |= (info.flags & C2FrameData::FLAG_END_OF_STREAM);
+ std::unique_ptr<C2Work> newWork = cloneInputWork(w, info.flags);
+ frameSet.insert(newFrameIdx);
+ newFrameIdx = mFrameIndex++;
+ newWork->input.ordinal.timestamp = info.timestamp;
+ au.push_back(multiAU.front().subBlock(offset, info.size));
+ if ((offset + info.size) > multiAUSize) {
+ LOG(ERROR) << "ERROR: access-unit offset > buffer size"
+ << " current offset " << (offset + info.size)
+ << " buffer size " << multiAUSize;
+ return C2_CORRUPTED;
+ }
+ newWork->input.buffers.push_back(
+ std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au)));
+ LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal "
+ << inputOrdinal.frameIndex.peekull()
+ << " total offset " << offset << " info.size " << info.size
+ << " : TS " << newWork->input.ordinal.timestamp.peekull();
+ // add to worklist
+ sliceWork.push_back(std::move(newWork));
+ processedWork->push_back(std::move(sliceWork));
+ offset += info.size;
+ }
+ if (!sendEos && (w->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
+ if (!processedWork->empty()) {
+ std::list<std::unique_ptr<C2Work>> &sliceWork = processedWork->back();
+ if (!sliceWork.empty()) {
+ std::unique_ptr<C2Work> &work = sliceWork.back();
+ if (work) {
+ work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
+ }
+ }
+ }
+ }
+ }
+ if (!processedWork->empty()) {
+ {
+ C2LargeFrame::output multiAccessParams = mInterface->getLargeFrameParam();
+ if (mInterface->kind() == C2Component::KIND_DECODER) {
+ uint32_t sampleRate = 0;
+ uint32_t channelCount = 0;
+ uint32_t frameSize = 0;
+ mInterface->getDecoderSampleRateAndChannelCount(
+ sampleRate, channelCount);
+ if (sampleRate > 0 && channelCount > 0) {
+ frameSize = channelCount * 2;
+ multiAccessParams.maxSize =
+ (multiAccessParams.maxSize / frameSize) * frameSize;
+ multiAccessParams.thresholdSize =
+ (multiAccessParams.thresholdSize / frameSize) * frameSize;
+ }
+ }
+ frameInfo.mLargeFrameTuning = multiAccessParams;
+ std::lock_guard<std::mutex> l(mLock);
+ mFrameHolder.push_back(std::move(frameInfo));
+ }
+ }
+ }
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::gather(
+ std::list<std::unique_ptr<C2Work>> &c2workItems,
+ std::list<std::unique_ptr<C2Work>>* const processedWork) {
+ LOG(DEBUG) << "Multi access-unit gather process";
+ if (processedWork == nullptr) {
+ LOG(ERROR) << "Nothing provided for processed work";
+ return C2_CORRUPTED;
+ }
+ auto addOutWork = [&processedWork](std::unique_ptr<C2Work>& work) {
+ processedWork->push_back(std::move(work));
+ };
+ {
+ std::lock_guard<std::mutex> l(mLock);
+ for (auto& work : c2workItems) {
+ LOG(DEBUG) << "FrameHolder Size: " << mFrameHolder.size();
+ uint64_t thisFrameIndex = work->input.ordinal.frameIndex.peekull();
+ bool removeEntry = work->worklets.empty()
+ || !work->worklets.front()
+ || (work->worklets.front()->output.flags
+ & C2FrameData::FLAG_INCOMPLETE) == 0;
+ bool foundFrame = false;
+ std::list<MultiAccessUnitInfo>::iterator frame =
+ mFrameHolder.begin();
+ while (!foundFrame && frame != mFrameHolder.end()) {
+ auto it = frame->mComponentFrameIds.find(thisFrameIndex);
+ if (it != frame->mComponentFrameIds.end()) {
+ foundFrame = true;
+ LOG(DEBUG) << "onWorkDone (frameIndex " << thisFrameIndex
+ << " worklstsSze " << work->worklets.size()
+ << ") -> frameIndex " << frame->inOrdinal.frameIndex.peekull();
+ if (work->result != C2_OK
+ || work->worklets.empty()
+ || !work->worklets.front()
+ || (frame->mLargeFrameTuning.thresholdSize == 0
+ || frame->mLargeFrameTuning.maxSize == 0)) {
+ if (removeEntry) {
+ frame->mComponentFrameIds.erase(it);
+ removeEntry = false;
+ }
+ if (frame->mLargeWork) {
+ finalizeWork(*frame);
+ addOutWork(frame->mLargeWork);
+ frame->reset();
+ }
+ c2_status_t workResult = work->result;
+ frame->mLargeWork = std::move(work);
+ frame->mLargeWork->input.ordinal.frameIndex =
+ frame->inOrdinal.frameIndex;
+ finalizeWork(*frame);
+ addOutWork(frame->mLargeWork);
+ frame->reset();
+ if (workResult != C2_OK) {
+ frame->mAccessUnitInfos.clear();
+ }
+ } else if (C2_OK != processWorklets(*frame, work, addOutWork)) {
+ LOG(DEBUG) << "Error while processing work";
+ }
+ if (removeEntry) {
+ LOG(DEBUG) << "Removing entry: " << thisFrameIndex
+ << " -> " << frame->inOrdinal.frameIndex.peekull();
+ frame->mComponentFrameIds.erase(it);
+ }
+ // This is to take care of the last bytes and to decide to send with
+ // FLAG_INCOMPLETE or not.
+ if ((frame->mWview
+ && (frame->mWview->offset() > frame->mLargeFrameTuning.thresholdSize))
+ || frame->mComponentFrameIds.empty()) {
+ if (frame->mLargeWork) {
+ finalizeWork(*frame);
+ addOutWork(frame->mLargeWork);
+ frame->reset();
+ }
+ }
+ if (frame->mComponentFrameIds.empty()) {
+ LOG(DEBUG) << "This frame is finished ID " << thisFrameIndex;
+ frame = mFrameHolder.erase(frame);
+ continue;
+ }
+ } else {
+ LOG(DEBUG) << "Received an out-of-order output " << thisFrameIndex
+ << " expected: " <<mFrameHolder.front().inOrdinal.frameIndex.peekull();
+ }
+ frame++;
+ }
+ if (!foundFrame) {
+ LOG(ERROR) <<" Error: Frame Holder reports no frame " << thisFrameIndex;
+ }
+ }
+ }
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::createLinearBlock(MultiAccessUnitInfo &frame) {
+ if (!mInit) {
+ LOG(ERROR) << "Large buffer allocator failed";
+ return C2_NO_MEMORY;
+ }
+ C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
+ uint32_t maxOutSize = frame.mLargeFrameTuning.maxSize;
+ c2_status_t err = mLinearPool->fetchLinearBlock(maxOutSize, usage, &frame.mBlock);
+ LOG(DEBUG) << "Allocated block with offset : " << frame.mBlock->offset()
+ << " size " << frame.mBlock->size() << " Capacity " << frame.mBlock->capacity();
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error allocating Multi access-unit Buffer";
+ return err;
+ }
+ frame.mWview = std::make_shared<C2WriteView>(frame.mBlock->map().get());
+ LOG(DEBUG) << "Allocated buffer : requested size : " <<
+ frame.mLargeFrameTuning.maxSize
+ << " alloc size " << frame.mWview->size();
+ return C2_OK;
+}
+
+/*
+ * For every work from the component, we try to do aggregation of work here.
+*/
+c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame,
+ std::unique_ptr<C2Work>& work,
+ const std::function <void(std::unique_ptr<C2Work>&)>& addWork) {
+ // This will allocate work, worklet, c2Block
+ auto allocateWork = [&](MultiAccessUnitInfo &frame,
+ bool allocateWorket = false,
+ bool allocateBuffer = false) {
+ c2_status_t ret = C2_OK;
+ if (frame.mLargeWork == nullptr) {
+ frame.mLargeWork.reset(new C2Work);
+ frame.mLargeWork->input.ordinal = frame.inOrdinal;
+ frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
+ }
+ if (allocateWorket) {
+ if (frame.mLargeWork->worklets.size() == 0) {
+ frame.mLargeWork->worklets.emplace_back(new C2Worklet);
+ }
+ }
+ if (allocateBuffer) {
+ if (frame.mWview == nullptr) {
+ ret = createLinearBlock(frame);
+ }
+ }
+ return ret;
+ };
+ // we will only have one worklet.
+ bool foundEndOfStream = false;
+ for (auto worklet = work->worklets.begin();
+ worklet != work->worklets.end() && (*worklet) != nullptr; ++worklet) {
+ uint32_t flagsForNoCopy = C2FrameData::FLAG_DROP_FRAME
+ | C2FrameData::FLAG_DISCARD_FRAME
+ | C2FrameData::FLAG_CORRUPT;
+ if ((*worklet)->output.flags & flagsForNoCopy) {
+ if (frame.mLargeWork) {
+ finalizeWork(frame);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ }
+ frame.mLargeWork = std::move(work);
+ frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
+ finalizeWork(frame);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ return C2_OK;
+ }
+ c2_status_t c2ret = allocateWork(frame, true);
+ if (c2ret != C2_OK) {
+ return c2ret;
+ }
+ C2FrameData& outputFramedata = frame.mLargeWork->worklets.front()->output;
+ if (!(*worklet)->output.configUpdate.empty()) {
+ for (auto& configUpdate : (*worklet)->output.configUpdate) {
+ outputFramedata.configUpdate.push_back(std::move(configUpdate));
+ }
+ (*worklet)->output.configUpdate.clear();
+ }
+ outputFramedata.infoBuffers.insert(outputFramedata.infoBuffers.begin(),
+ (*worklet)->output.infoBuffers.begin(),
+ (*worklet)->output.infoBuffers.end());
+ int64_t sampleTimeUs = 0;
+ uint32_t frameSize = 0;
+ uint32_t sampleRate = 0;
+ uint32_t channelCount = 0;
+ mInterface->getDecoderSampleRateAndChannelCount(sampleRate, channelCount);
+ if (sampleRate > 0 && channelCount > 0) {
+ sampleTimeUs = (1000000u) / (sampleRate * channelCount * 2);
+ frameSize = channelCount * 2;
+ }
+ LOG(DEBUG) << "maxOutSize " << frame.mLargeFrameTuning.maxSize
+ << " threshold " << frame.mLargeFrameTuning.thresholdSize;
+ if ((*worklet)->output.buffers.size() > 0) {
+ allocateWork(frame, true, true);
+ }
+ LOG(DEBUG) << "This worklet has " << (*worklet)->output.buffers.size() << " buffers"
+ << " ts: " << (*worklet)->output.ordinal.timestamp.peekull();
+ int64_t workletTimestamp = (*worklet)->output.ordinal.timestamp.peekull();
+ int64_t timestamp = workletTimestamp;
+ uint32_t flagsForCopy = ((*worklet)->output.flags) & C2FrameData::FLAG_CODEC_CONFIG;
+ for (int bufIdx = 0; bufIdx < (*worklet)->output.buffers.size(); ++bufIdx) {
+ std::shared_ptr<C2Buffer>& buffer = (*worklet)->output.buffers[bufIdx];
+ if (!buffer || buffer->data().linearBlocks().empty()) {
+ continue;
+ }
+ const std::vector<C2ConstLinearBlock>& blocks = buffer->data().linearBlocks();
+ if (blocks.size() > 0) {
+ uint32_t inputOffset = 0;
+ uint32_t inputSize = blocks.front().size();
+ frame.mInfos.insert(frame.mInfos.end(),
+ buffer->info().begin(), buffer->info().end());
+ if (frameSize != 0 && (mInterface->kind() == C2Component::KIND_DECODER)) {
+ // For decoders we only split multiples of 16bChannelCount*2
+ inputSize -= (inputSize % frameSize);
+ }
+ while (inputOffset < inputSize) {
+ if (frame.mWview->offset() >= frame.mLargeFrameTuning.thresholdSize) {
+ frame.mLargeWork->result = C2_OK;
+ finalizeWork(frame, flagsForCopy);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ allocateWork(frame, true, true);
+ }
+ if (mInterface->kind() == C2Component::KIND_ENCODER) {
+ if (inputSize > frame.mLargeFrameTuning.maxSize) {
+ LOG(ERROR) << "Enc: Output buffer too small for AU, configured with "
+ << frame.mLargeFrameTuning.maxSize
+ << " block size: " << blocks.front().size()
+ << "alloc size " << frame.mWview->size();
+ if (frame.mLargeWork
+ && frame.mWview && frame.mWview->offset() > 0) {
+ finalizeWork(frame, flagsForCopy);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ allocateWork(frame, true, false);
+ }
+ frame.mLargeWork->result = C2_NO_MEMORY;
+ finalizeWork(frame, 0, true);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ return C2_NO_MEMORY;
+ } else if (inputSize > frame.mWview->size()) {
+ LOG(DEBUG) << "Enc: Large frame hitting bufer limit, current size "
+ << frame.mWview->offset();
+ if (frame.mLargeWork
+ && frame.mWview && frame.mWview->offset() > 0) {
+ finalizeWork(frame, flagsForCopy);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ allocateWork(frame, true, true);
+ }
+ }
+ }
+ C2ReadView rView = blocks.front().map().get();
+ if (rView.error()) {
+ LOG(ERROR) << "Buffer read view error";
+ frame.mLargeWork->result = rView.error();
+ frame.mLargeWork->worklets.clear();
+ finalizeWork(frame, 0, true);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ return C2_NO_MEMORY;
+ }
+ uint32_t toCopy = 0;
+ if (mInterface->kind() == C2Component::KIND_ENCODER) {
+ toCopy = inputSize;
+ } else {
+ toCopy = c2_min(frame.mWview->size(), (inputSize - inputOffset));
+ timestamp = workletTimestamp + inputOffset * sampleTimeUs;
+ LOG(DEBUG) << "ts " << timestamp
+ << " copiedOutput " << inputOffset
+ << " sampleTimeUs " << sampleTimeUs;
+ }
+ LOG(DEBUG) << " Copy size " << toCopy
+ << " ts " << timestamp;
+ memcpy(frame.mWview->data(), rView.data() + inputOffset, toCopy);
+ frame.mWview->setOffset(frame.mWview->offset() + toCopy);
+ inputOffset += toCopy;
+ mergeAccessUnitInfo(frame, flagsForCopy, toCopy, timestamp);
+ }
+ } else {
+ frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(buffer));
+ LOG(DEBUG) << "Copying worklets without linear buffer";
+ }
+ }
+ uint32_t flagsForCsdOrEnd = (*worklet)->output.flags
+ & (C2FrameData::FLAG_END_OF_STREAM | C2FrameData::FLAG_CODEC_CONFIG);
+ if (flagsForCsdOrEnd) {
+ LOG(DEBUG) << "Output worklet has CSD/EOS data";
+ frame.mLargeWork->result = C2_OK;
+ // we can assign timestamp as this will be evaluated in finalizeWork
+ frame.mLargeWork->worklets.front()->output.ordinal.timestamp = timestamp;
+ finalizeWork(frame, flagsForCsdOrEnd, true);
+ addWork(frame.mLargeWork);
+ frame.reset();
+ }
+ }
+ return C2_OK;
+}
+
+c2_status_t MultiAccessUnitHelper::finalizeWork(
+ MultiAccessUnitInfo& frame, uint32_t inFlags, bool forceComplete) {
+ if (frame.mLargeWork == nullptr) {
+ return C2_OK;
+ }
+ //prepare input ordinal
+ frame.mLargeWork->input.ordinal = frame.inOrdinal;
+ // remove this
+ int64_t timeStampUs = frame.inOrdinal.timestamp.peekull();
+ if (!frame.mAccessUnitInfos.empty()) {
+ timeStampUs = frame.mAccessUnitInfos.front().timestamp;
+ } else if (!frame.mLargeWork->worklets.empty()) {
+ std::unique_ptr<C2Worklet> &worklet = frame.mLargeWork->worklets.front();
+ if (worklet) {
+ timeStampUs = worklet->output.ordinal.timestamp.peekull();
+ }
+ }
+ LOG(DEBUG) << "Finalizing work with input Idx "
+ << frame.mLargeWork->input.ordinal.frameIndex.peekull()
+ << " timestamp " << timeStampUs;
+ uint32_t finalFlags = 0;
+ if ((!forceComplete)
+ && (frame.mLargeWork->result == C2_OK)
+ && (!frame.mComponentFrameIds.empty())) {
+ finalFlags |= C2FrameData::FLAG_INCOMPLETE;
+ }
+ if (frame.mLargeWork->result == C2_OK) {
+ finalFlags |= inFlags;
+ }
+ // update worklet if present
+ if (!frame.mLargeWork->worklets.empty() &&
+ frame.mLargeWork->worklets.front() != nullptr) {
+ frame.mLargeWork->workletsProcessed = 1;
+ C2FrameData& outFrameData = frame.mLargeWork->worklets.front()->output;
+ outFrameData.ordinal.frameIndex = frame.inOrdinal.frameIndex.peekull();
+ outFrameData.ordinal.timestamp = timeStampUs;
+ finalFlags |= frame.mLargeWork->worklets.front()->output.flags;
+ outFrameData.flags = (C2FrameData::flags_t)finalFlags;
+ // update buffers
+ if (frame.mBlock && (frame.mWview->offset() > 0)) {
+ size_t size = frame.mWview->offset();
+ LOG(DEBUG) << "Finalize : Block: Large frame size set as " << size
+ << " timestamp as " << timeStampUs
+ << "frameIndex " << outFrameData.ordinal.frameIndex.peekull();
+ frame.mWview->setOffset(0);
+ std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateLinearBuffer(
+ frame.mBlock->share(0, size, ::C2Fence()));
+ if (frame.mAccessUnitInfos.size() > 0) {
+ if (finalFlags & C2FrameData::FLAG_END_OF_STREAM) {
+ frame.mAccessUnitInfos.back().flags |=
+ C2FrameData::FLAG_END_OF_STREAM;
+ }
+ std::shared_ptr<C2AccessUnitInfos::output> largeFrame =
+ C2AccessUnitInfos::output::AllocShared(
+ frame.mAccessUnitInfos.size(), 0u, frame.mAccessUnitInfos);
+ frame.mInfos.push_back(largeFrame);
+ frame.mAccessUnitInfos.clear();
+ }
+ for (auto &info : frame.mInfos) {
+ c2Buffer->setInfo(std::const_pointer_cast<C2Info>(info));
+ }
+ frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(c2Buffer));
+ frame.mInfos.clear();
+ frame.mBlock.reset();
+ frame.mWview.reset();
+ }
+ }
+ LOG(DEBUG) << "Multi access-unitflag setting as " << finalFlags;
+ return C2_OK;
+}
+
+void MultiAccessUnitHelper::mergeAccessUnitInfo(
+ MultiAccessUnitInfo &frame,
+ uint32_t flags_,
+ uint32_t size,
+ int64_t timestamp) {
+ // Remove flags that are not part of Access unit info
+ uint32_t flags = flags_ & ~(C2FrameData::FLAG_INCOMPLETE
+ | C2FrameData::FLAG_DISCARD_FRAME
+ | C2FrameData::FLAG_CORRUPT
+ | C2FrameData::FLAG_CORRECTED);
+ if (frame.mAccessUnitInfos.empty()) {
+ frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
+ return;
+ }
+ if ((mInterface->kind() == C2Component::KIND_DECODER) &&
+ (frame.mAccessUnitInfos.back().flags == flags)) {
+ // merge access units here
+ C2AccessUnitInfosStruct &s = frame.mAccessUnitInfos.back();
+ s.size += size; // don't have to update timestamp
+ } else {
+ frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
+ }
+}
+
+void MultiAccessUnitHelper::MultiAccessUnitInfo::reset() {
+ mBlock.reset();
+ mWview.reset();
+ mInfos.clear();
+ mAccessUnitInfos.clear();
+ mLargeWork.reset();
+}
+
+} // namespace android
\ No newline at end of file
diff --git a/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h b/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h
new file mode 100644
index 0000000..ef5cff9
--- /dev/null
+++ b/media/codec2/hal/common/include/codec2/common/MultiAccessUnitHelper.h
@@ -0,0 +1,211 @@
+/*
+ * Copyright 2023 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 CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
+#define CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
+
+#include <hidl/Status.h>
+#include <hwbinder/IBinder.h>
+
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
+#include <C2Buffer.h>
+#include <C2.h>
+
+#include <set>
+#include <memory>
+#include <mutex>
+
+namespace android {
+
+struct MultiAccessUnitHelper;
+
+struct MultiAccessUnitInterface : public C2InterfaceHelper {
+ explicit MultiAccessUnitInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ std::shared_ptr<C2ReflectorHelper> helper);
+
+ bool isParamSupported(C2Param::Index index);
+ C2LargeFrame::output getLargeFrameParam() const;
+ C2Component::kind_t kind() const;
+
+protected:
+ void getDecoderSampleRateAndChannelCount(
+ uint32_t &sampleRate_, uint32_t &channelCount_) const;
+ const std::shared_ptr<C2ComponentInterface> mC2ComponentIntf;
+ std::shared_ptr<C2LargeFrame::output> mLargeFrameParams;
+ C2ComponentKindSetting mKind;
+ std::set<C2Param::Index> mSupportedParamIndexSet;
+
+ friend struct MultiAccessUnitHelper;
+};
+
+struct MultiAccessUnitHelper {
+public:
+ MultiAccessUnitHelper(
+ const std::shared_ptr<MultiAccessUnitInterface>& intf);
+
+ virtual ~MultiAccessUnitHelper();
+
+ static bool isEnabledOnPlatform();
+
+ /*
+ * Scatters the incoming linear buffer into access-unit sized buffers
+ * based on the access-unit info.
+ */
+ c2_status_t scatter(
+ std::list<std::unique_ptr<C2Work>> &c2workItems,
+ std::list<std::list<std::unique_ptr<C2Work>>> * const processedWork);
+
+ /*
+ * Gathers different access-units into a single buffer based on the scatter list
+ * and the configured max and threshold sizes. This also generates the associated
+ * access-unit information and attach it with the final result.
+ */
+ c2_status_t gather(
+ std::list<std::unique_ptr<C2Work>> &c2workItems,
+ std::list<std::unique_ptr<C2Work>> * const processedWork);
+
+ /*
+ * Flushes the codec and generated the list of flushed buffers.
+ */
+ c2_status_t flush(
+ std::list<std::unique_ptr<C2Work>> * const c2flushedWorks);
+
+ /*
+ * Gets all the pending buffers under generation in c2workItems.
+ */
+ c2_status_t error(std::list<std::unique_ptr<C2Work>> * const c2workItems);
+
+ /*
+ * Get the interface object of this handler.
+ */
+ std::shared_ptr<MultiAccessUnitInterface> getInterface();
+
+ /*
+ * Gets the status of the object. This really is to make sure that
+ * all the allocators are configured properly within the handler.
+ */
+ bool getStatus();
+
+ /*
+ * Resets the structures inside the handler.
+ */
+ void reset();
+
+protected:
+
+ struct MultiAccessUnitInfo {
+ /*
+ * From the input
+ * Ordinal of the input frame
+ */
+ C2WorkOrdinalStruct inOrdinal;
+
+ /*
+ * Frame indexes of the scattered buffers
+ */
+ std::set<uint64_t> mComponentFrameIds;
+
+ /*
+ * For the output
+ * Current output block.
+ */
+ std::shared_ptr<C2LinearBlock> mBlock;
+
+ /*
+ * Write view of current block
+ */
+ std::shared_ptr<C2WriteView> mWview;
+
+ /*
+ * C2Info related to the current mBlock
+ */
+ std::vector<std::shared_ptr<const C2Info>> mInfos;
+
+ /*
+ * C2AccessUnitInfos for the current buffer
+ */
+ std::vector<C2AccessUnitInfosStruct> mAccessUnitInfos;
+
+ /*
+ * Current tuning used to process this input work
+ */
+ C2LargeFrame::output mLargeFrameTuning;
+
+ /*
+ * Current output C2Work being processed
+ */
+ std::unique_ptr<C2Work> mLargeWork;
+
+ MultiAccessUnitInfo(C2WorkOrdinalStruct ordinal):inOrdinal(ordinal) {
+
+ }
+
+ /*
+ * Resets this frame
+ */
+ void reset();
+ };
+
+ /*
+ * Creates a linear block to be used with work
+ */
+ c2_status_t createLinearBlock(MultiAccessUnitInfo &frame);
+
+ /*
+ * Processes worklets from the component
+ */
+ c2_status_t processWorklets(MultiAccessUnitInfo &frame,
+ std::unique_ptr<C2Work> &work,
+ const std::function <void(std::unique_ptr<C2Work>&)> &addWork);
+
+ /*
+ * Finalizes the work to be send out.
+ */
+ c2_status_t finalizeWork(MultiAccessUnitInfo &frame,
+ uint32_t flags = 0, bool forceComplete = false);
+
+ /*
+ * Merges different access unit infos if possible
+ */
+ void mergeAccessUnitInfo(MultiAccessUnitInfo &frame,
+ uint32_t flags,
+ uint32_t size,
+ int64_t timestamp);
+
+ bool mInit;
+
+ // Interface of this module
+ std::shared_ptr<MultiAccessUnitInterface> mInterface;
+ // Local pool id used for output buffer allocation
+ C2BlockPool::local_id_t mBlockPoolId;
+ // C2Blockpool for output buffer allocation
+ std::shared_ptr<C2BlockPool> mLinearPool;
+ // Allocator for output buffer allocation
+ std::shared_ptr<C2Allocator> mLinearAllocator;
+ // FrameIndex for the current outgoing work
+ std::atomic_uint64_t mFrameIndex;
+ // Mutex to protect mFrameHolder
+ std::mutex mLock;
+ // List of Infos that contains the input and
+ // output work and buffer objects
+ std::list<MultiAccessUnitInfo> mFrameHolder;
+};
+
+} // namespace android
+
+#endif // CODEC2_COMMON_MULTI_ACCESSUNIT_HELPER_H
diff --git a/media/codec2/hal/hidl/1.0/utils/Android.bp b/media/codec2/hal/hidl/1.0/utils/Android.bp
index 2f2ecd1..9646a0b 100644
--- a/media/codec2/hal/hidl/1.0/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.0/utils/Android.bp
@@ -52,7 +52,6 @@
],
}
-
// DO NOT DEPEND ON THIS DIRECTLY
// use libcodec2-hidl-defaults instead
cc_library {
diff --git a/media/codec2/hal/hidl/1.0/utils/Component.cpp b/media/codec2/hal/hidl/1.0/utils/Component.cpp
index 0aeed08..ebbaafc 100644
--- a/media/codec2/hal/hidl/1.0/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/Component.cpp
@@ -135,6 +135,52 @@
wp<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const sp<Component> &component,
+ const std::shared_ptr<MultiAccessUnitHelper> &handler):
+ Listener(component), mHandler(handler) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHandler) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHandler->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHandler) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHandler->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHandler;
+};
+
// Component::Sink
struct Component::Sink : public IInputSink {
std::shared_ptr<Component> mComponent;
@@ -208,13 +254,37 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(),
- store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = new ComponentInterface(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -240,7 +310,6 @@
// Methods from ::android::hardware::media::c2::V1_0::IComponent
Return<Status> Component::queue(const WorkBundle& workBundle) {
std::list<std::unique_ptr<C2Work>> c2works;
-
if (!objcpy(&c2works, workBundle)) {
return Status::CORRUPTED;
}
@@ -252,7 +321,19 @@
registerFrameData(mListener, work->input);
}
}
-
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ break;
+ }
+ }
+ return static_cast<Status>(err);
+ }
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
@@ -261,6 +342,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
@@ -469,6 +553,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -479,6 +566,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -501,8 +591,14 @@
}
void Component::initListener(const sp<Component>& self) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
+
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
mInit = res;
diff --git a/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp b/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
index 12078e0..5a5e780 100644
--- a/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
+++ b/media/codec2/hal/hidl/1.0/utils/ComponentInterface.cpp
@@ -25,7 +25,6 @@
#include <hidl/HidlBinderSupport.h>
#include <utils/Timers.h>
-#include <C2BqBufferPriv.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -45,9 +44,10 @@
// Implementation of ConfigurableC2Intf based on C2ComponentInterface
struct CompIntf : public ConfigurableC2Intf {
- CompIntf(const std::shared_ptr<C2ComponentInterface>& intf) :
+ CompIntf(const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf):
ConfigurableC2Intf{intf->getName(), intf->getId()},
- mIntf{intf} {
+ mIntf{intf}, mMultiAccessUnitIntf{multiAccessUnitIntf} {
}
virtual c2_status_t config(
@@ -55,7 +55,34 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2SettingResult>>* const failures
) override {
- return mIntf->config_vb(params, mayBlock, failures);
+ std::vector<C2Param*> paramsToIntf;
+ std::vector<C2Param*> paramsToLargeFrameIntf;
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->config_vb(params, mayBlock, failures);
+ return err;
+ }
+ for (auto &p : params) {
+ if (mMultiAccessUnitIntf->isParamSupported(p->index())) {
+ paramsToLargeFrameIntf.push_back(p);
+ } else {
+ paramsToIntf.push_back(p);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->config_vb(paramsToIntf, mayBlock, failures);
+ }
+ if (err1 != C2_OK) {
+ LOG(ERROR) << "We have a failed config";
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->config(
+ paramsToLargeFrameIntf, mayBlock, failures);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t query(
@@ -63,33 +90,74 @@
c2_blocking_t mayBlock,
std::vector<std::unique_ptr<C2Param>>* const params
) const override {
- return mIntf->query_vb({}, indices, mayBlock, params);
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitIntf == nullptr) {
+ err = mIntf->query_vb({}, indices, mayBlock, params);
+ return err;
+ }
+ std::vector<C2Param::Index> paramsToIntf;
+ std::vector<C2Param::Index> paramsToLargeFrameIntf;
+ for (auto &i : indices) {
+ if (mMultiAccessUnitIntf->isParamSupported(i)) {
+ paramsToLargeFrameIntf.push_back(i);
+ } else {
+ paramsToIntf.push_back(i);
+ }
+ }
+ c2_status_t err1 = C2_OK;
+ if (paramsToIntf.size() > 0) {
+ err1 = mIntf->query_vb({}, paramsToIntf, mayBlock, params);
+ }
+ c2_status_t err2 = C2_OK;
+ if (paramsToLargeFrameIntf.size() > 0) {
+ err2 = mMultiAccessUnitIntf->query(
+ {}, paramsToLargeFrameIntf, mayBlock, params);
+ }
+ // TODO: correct failure vector
+ return err1 != C2_OK ? err1 : err2;
}
virtual c2_status_t querySupportedParams(
std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
) const override {
- return mIntf->querySupportedParams_nb(params);
+ c2_status_t err = mIntf->querySupportedParams_nb(params);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedParams(params);
+ }
+ return err;
}
virtual c2_status_t querySupportedValues(
std::vector<C2FieldSupportedValuesQuery>& fields,
c2_blocking_t mayBlock) const override {
- return mIntf->querySupportedValues_vb(fields, mayBlock);
+ c2_status_t err = mIntf->querySupportedValues_vb(fields, mayBlock);
+ if (mMultiAccessUnitIntf != nullptr) {
+ err = mMultiAccessUnitIntf->querySupportedValues(fields, mayBlock);
+ }
+ return err;
}
protected:
std::shared_ptr<C2ComponentInterface> mIntf;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
};
} // unnamed namespace
+
// ComponentInterface
ComponentInterface::ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<ParameterCache>& cache):ComponentInterface(intf, nullptr, cache) {
+}
+
+ComponentInterface::ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& intf,
+ const std::shared_ptr<MultiAccessUnitInterface>& multiAccessUnitIntf,
const std::shared_ptr<ParameterCache>& cache)
: mInterface{intf},
- mConfigurable{new CachedConfigurable(std::make_unique<CompIntf>(intf))} {
+ mConfigurable{new CachedConfigurable(
+ std::make_unique<CompIntf>(intf, multiAccessUnitIntf))} {
mInit = mConfigurable->init(cache);
}
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
index 3f55618..aed94ec 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/Component.h
@@ -30,6 +30,8 @@
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -54,6 +56,8 @@
using ::android::hardware::IBinder;
using ::android::sp;
using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -113,6 +117,8 @@
std::shared_ptr<C2Component> mComponent;
sp<ComponentInterface> mInterface;
sp<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
sp<ComponentStore> mStore;
::android::hardware::media::c2::V1_0::utils::DefaultBufferPoolSender
mBufferPoolSender;
@@ -135,6 +141,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
sp<HwDeathRecipient> mDeathRecipient;
bool mClientDied{false};
diff --git a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
index 9102f92..2995faf 100644
--- a/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
+++ b/media/codec2/hal/hidl/1.0/utils/include/codec2/hidl/1.0/ComponentInterface.h
@@ -23,10 +23,15 @@
#include <android/hardware/media/c2/1.0/IComponentInterface.h>
#include <hidl/Status.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
+#include <C2Config.h>
+#include <util/C2InterfaceHelper.h>
#include <C2.h>
+#include <set>
#include <memory>
namespace android {
@@ -39,6 +44,7 @@
using ::android::hardware::Return;
using ::android::hardware::Void;
using ::android::sp;
+using ::android::MultiAccessUnitInterface;
struct ComponentStore;
@@ -46,6 +52,11 @@
ComponentInterface(
const std::shared_ptr<C2ComponentInterface>& interface,
const std::shared_ptr<ParameterCache>& cache);
+
+ ComponentInterface(
+ const std::shared_ptr<C2ComponentInterface>& interface,
+ const std::shared_ptr<MultiAccessUnitInterface>& largeBufferIntf,
+ const std::shared_ptr<ParameterCache>& cache);
c2_status_t status() const;
virtual Return<sp<IConfigurable>> getConfigurable() override;
diff --git a/media/codec2/hal/hidl/1.1/utils/Android.bp b/media/codec2/hal/hidl/1.1/utils/Android.bp
index 4f86511..d8b5db5 100644
--- a/media/codec2/hal/hidl/1.1/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.1/utils/Android.bp
@@ -54,7 +54,6 @@
],
}
-
// DO NOT DEPEND ON THIS DIRECTLY
// use libcodec2-hidl-defaults instead
cc_library {
@@ -66,7 +65,6 @@
"com.android.media.swcodec",
],
-
defaults: ["hidl_defaults"],
srcs: [
@@ -97,6 +95,7 @@
"android.hardware.media.omx@1.0",
"libbase",
"libcodec2",
+ "libcodec2_hal_common",
"libcodec2_hidl@1.0",
"libcodec2_hidl_plugin_stub",
"libcodec2_vndk",
@@ -173,4 +172,3 @@
"libhidlbase",
],
}
-
diff --git a/media/codec2/hal/hidl/1.1/utils/Component.cpp b/media/codec2/hal/hidl/1.1/utils/Component.cpp
index d0f4f19..5073983 100644
--- a/media/codec2/hal/hidl/1.1/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.1/utils/Component.cpp
@@ -29,6 +29,8 @@
#include <hidl/HidlBinderSupport.h>
#include <utils/Timers.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2BqBufferPriv.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
@@ -44,6 +46,8 @@
namespace utils {
using namespace ::android;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
@@ -135,6 +139,52 @@
wp<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const sp<Component> &component,
+ const std::shared_ptr<MultiAccessUnitHelper> &helper):
+ Listener(component), mHelper(helper) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHelper->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHelper->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
// Component::Sink
struct Component::Sink : public IInputSink {
std::shared_ptr<Component> mComponent;
@@ -208,13 +258,37 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(),
- store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = new ComponentInterface(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -252,6 +326,19 @@
registerFrameData(mListener, work->input);
}
}
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ break;
+ }
+ }
+ return static_cast<Status>(err);
+ }
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
@@ -261,6 +348,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
@@ -469,6 +559,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -479,6 +572,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -508,7 +604,12 @@
}
void Component::initListener(const sp<Component>& self) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
diff --git a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
index f16de24..8f0478f 100644
--- a/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
+++ b/media/codec2/hal/hidl/1.1/utils/include/codec2/hidl/1.1/Component.h
@@ -29,6 +29,8 @@
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -57,6 +59,8 @@
using ::android::hardware::IBinder;
using ::android::sp;
using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -118,6 +122,8 @@
std::shared_ptr<C2Component> mComponent;
sp<ComponentInterface> mInterface;
sp<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
sp<ComponentStore> mStore;
::android::hardware::media::c2::V1_1::utils::DefaultBufferPoolSender
mBufferPoolSender;
@@ -140,6 +146,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
sp<HwDeathRecipient> mDeathRecipient;
bool mClientDied{false};
diff --git a/media/codec2/hal/hidl/1.2/utils/Android.bp b/media/codec2/hal/hidl/1.2/utils/Android.bp
index b92dc07..a339946 100644
--- a/media/codec2/hal/hidl/1.2/utils/Android.bp
+++ b/media/codec2/hal/hidl/1.2/utils/Android.bp
@@ -58,7 +58,6 @@
],
}
-
// DO NOT DEPEND ON THIS DIRECTLY
// use libcodec2-hidl-defaults instead
cc_library {
@@ -102,6 +101,7 @@
"android.hardware.media.omx@1.0",
"libbase",
"libcodec2",
+ "libcodec2_hal_common",
"libcodec2_hidl@1.0",
"libcodec2_hidl@1.1",
"libcodec2_hidl_plugin_stub",
@@ -197,4 +197,3 @@
name: "libcodec2-hidl-client-defaults",
defaults: ["libcodec2-hidl-client-defaults@1.2"],
}
-
diff --git a/media/codec2/hal/hidl/1.2/utils/Component.cpp b/media/codec2/hal/hidl/1.2/utils/Component.cpp
index 036c900..bbdbef5 100644
--- a/media/codec2/hal/hidl/1.2/utils/Component.cpp
+++ b/media/codec2/hal/hidl/1.2/utils/Component.cpp
@@ -44,6 +44,8 @@
namespace utils {
using namespace ::android;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
// ComponentListener wrapper
struct Component::Listener : public C2Component::Listener {
@@ -135,6 +137,52 @@
wp<IComponentListener> mListener;
};
+// Component listener for handle multiple access-units
+struct MultiAccessUnitListener : public Component::Listener {
+ MultiAccessUnitListener(const sp<Component> &component,
+ const std::shared_ptr<MultiAccessUnitHelper> &helper):
+ Listener(component), mHelper(helper) {
+ }
+
+ virtual void onError_nb(
+ std::weak_ptr<C2Component> c2component,
+ uint32_t errorCode) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> worklist;
+ mHelper->error(&worklist);
+ if (!worklist.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(worklist));
+ }
+ }
+ Listener::onError_nb(c2component, errorCode);
+ }
+
+ virtual void onTripped_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::vector<std::shared_ptr<C2SettingResult>> c2settingResult
+ ) override {
+ Listener::onTripped_nb(c2component,
+ c2settingResult);
+ }
+
+ virtual void onWorkDone_nb(
+ std::weak_ptr<C2Component> c2component,
+ std::list<std::unique_ptr<C2Work>> c2workItems) override {
+ if (mHelper) {
+ std::list<std::unique_ptr<C2Work>> processedWork;
+ mHelper->gather(c2workItems, &processedWork);
+ if (!processedWork.empty()) {
+ Listener::onWorkDone_nb(c2component, std::move(processedWork));
+ }
+ } else {
+ Listener::onWorkDone_nb(c2component, std::move(c2workItems));
+ }
+ }
+
+ protected:
+ std::shared_ptr<MultiAccessUnitHelper> mHelper;
+};
+
// Component::Sink
struct Component::Sink : public IInputSink {
std::shared_ptr<Component> mComponent;
@@ -208,13 +256,37 @@
const sp<::android::hardware::media::bufferpool::V2_0::
IClientManager>& clientPoolManager)
: mComponent{component},
- mInterface{new ComponentInterface(component->intf(),
- store->getParameterCache())},
mListener{listener},
mStore{store},
mBufferPoolSender{clientPoolManager} {
// Retrieve supported parameters from store
// TODO: We could cache this per component/interface type
+ if (MultiAccessUnitHelper::isEnabledOnPlatform()) {
+ c2_status_t err = C2_OK;
+ C2ComponentDomainSetting domain;
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ err = component->intf()->query_vb({&domain}, {}, C2_MAY_BLOCK, &heapParams);
+ if (err == C2_OK && (domain.value == C2Component::DOMAIN_AUDIO)) {
+ std::vector<std::shared_ptr<C2ParamDescriptor>> params;
+ bool isComponentSupportsLargeAudioFrame = false;
+ component->intf()->querySupportedParams_nb(¶ms);
+ for (const auto ¶mDesc : params) {
+ if (paramDesc->name().compare(C2_PARAMKEY_OUTPUT_LARGE_FRAME) == 0) {
+ isComponentSupportsLargeAudioFrame = true;
+ LOG(VERBOSE) << "Underlying component supports large frame audio";
+ break;
+ }
+ }
+ if (!isComponentSupportsLargeAudioFrame) {
+ mMultiAccessUnitIntf = std::make_shared<MultiAccessUnitInterface>(
+ component->intf(),
+ std::static_pointer_cast<C2ReflectorHelper>(
+ GetCodec2PlatformComponentStore()->getParamReflector()));
+ }
+ }
+ }
+ mInterface = new ComponentInterface(
+ component->intf(), mMultiAccessUnitIntf, store->getParameterCache());
mInit = mInterface->status();
}
@@ -252,7 +324,19 @@
registerFrameData(mListener, work->input);
}
}
-
+ c2_status_t err = C2_OK;
+ if (mMultiAccessUnitHelper) {
+ std::list<std::list<std::unique_ptr<C2Work>>> c2worklists;
+ mMultiAccessUnitHelper->scatter(c2works, &c2worklists);
+ for (auto &c2worklist : c2worklists) {
+ err = mComponent->queue_nb(&c2worklist);
+ if (err != C2_OK) {
+ LOG(ERROR) << "Error Queuing to component.";
+ break;
+ }
+ }
+ return static_cast<Status>(err);
+ }
return static_cast<Status>(mComponent->queue_nb(&c2works));
}
@@ -261,7 +345,9 @@
c2_status_t c2res = mComponent->flush_sm(
C2Component::FLUSH_COMPONENT,
&c2flushedWorks);
-
+ if (mMultiAccessUnitHelper) {
+ c2res = mMultiAccessUnitHelper->flush(&c2flushedWorks);
+ }
// Unregister input buffers.
for (const std::unique_ptr<C2Work>& work : c2flushedWorks) {
if (work) {
@@ -469,6 +555,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -479,6 +568,9 @@
std::lock_guard<std::mutex> lock(mBlockPoolsMutex);
mBlockPools.clear();
}
+ if (mMultiAccessUnitHelper) {
+ mMultiAccessUnitHelper->reset();
+ }
InputBufferManager::unregisterFrameData(mListener);
return status;
}
@@ -539,7 +631,12 @@
}
void Component::initListener(const sp<Component>& self) {
- std::shared_ptr<C2Component::Listener> c2listener =
+ std::shared_ptr<C2Component::Listener> c2listener;
+ if (mMultiAccessUnitIntf) {
+ mMultiAccessUnitHelper = std::make_shared<MultiAccessUnitHelper>(mMultiAccessUnitIntf);
+ }
+ c2listener = mMultiAccessUnitHelper ?
+ std::make_shared<MultiAccessUnitListener>(self, mMultiAccessUnitHelper) :
std::make_shared<Listener>(self);
c2_status_t res = mComponent->setListener_vb(c2listener, C2_DONT_BLOCK);
if (res != C2_OK) {
diff --git a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
index 6a73392..f2b77bc 100644
--- a/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
+++ b/media/codec2/hal/hidl/1.2/utils/include/codec2/hidl/1.2/Component.h
@@ -29,6 +29,8 @@
#include <hidl/Status.h>
#include <hwbinder/IBinder.h>
+#include <codec2/common/MultiAccessUnitHelper.h>
+
#include <C2Component.h>
#include <C2Buffer.h>
#include <C2.h>
@@ -57,6 +59,8 @@
using ::android::hardware::IBinder;
using ::android::sp;
using ::android::wp;
+using ::android::MultiAccessUnitInterface;
+using ::android::MultiAccessUnitHelper;
struct ComponentStore;
@@ -123,6 +127,8 @@
std::shared_ptr<C2Component> mComponent;
sp<ComponentInterface> mInterface;
sp<IComponentListener> mListener;
+ std::shared_ptr<MultiAccessUnitInterface> mMultiAccessUnitIntf;
+ std::shared_ptr<MultiAccessUnitHelper> mMultiAccessUnitHelper;
sp<ComponentStore> mStore;
::android::hardware::media::c2::V1_2::utils::DefaultBufferPoolSender
mBufferPoolSender;
@@ -145,6 +151,8 @@
struct Listener;
+ friend struct MultiAccessUnitListener;
+
using HwDeathRecipient = ::android::hardware::hidl_death_recipient;
sp<HwDeathRecipient> mDeathRecipient;
bool mClientDied{false};
diff --git a/media/codec2/sfplugin/CCodecBufferChannel.cpp b/media/codec2/sfplugin/CCodecBufferChannel.cpp
index 6b45e0e..7b1721e 100644
--- a/media/codec2/sfplugin/CCodecBufferChannel.cpp
+++ b/media/codec2/sfplugin/CCodecBufferChannel.cpp
@@ -90,6 +90,28 @@
return v == "true";
}
+// Flags can come with individual BufferInfos
+// when used with large frame audio
+constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
+ {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
+ {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
+ {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
+};
+
+static uint32_t convertFlags(uint32_t flags, bool toC2) {
+ return std::transform_reduce(
+ flagList.begin(), flagList.end(),
+ 0u,
+ std::bit_or{},
+ [flags, toC2](const std::pair<uint32_t, uint32_t> &entry) {
+ if (toC2) {
+ return (flags & entry.first) ? entry.second : 0;
+ } else {
+ return (flags & entry.second) ? entry.first : 0;
+ }
+ });
+}
+
} // namespace
CCodecBufferChannel::QueueGuard::QueueGuard(
@@ -245,7 +267,8 @@
if (buffer->meta()->findInt32("decode-only", &tmp) && tmp) {
flags |= C2FrameData::FLAG_DROP_FRAME;
}
- ALOGV("[%s] queueInputBuffer: buffer->size() = %zu", mName, buffer->size());
+ ALOGV("[%s] queueInputBuffer: buffer->size() = %zu time: %lld",
+ mName, buffer->size(), (long long)timeUs);
std::list<std::unique_ptr<C2Work>> items;
std::unique_ptr<C2Work> work(new C2Work);
work->input.ordinal.timestamp = timeUs;
@@ -296,6 +319,34 @@
uint64_t frameIndex = work->input.ordinal.frameIndex.peeku();
output->rotation[frameIndex] = rotation;
}
+ sp<RefBase> obj;
+ if (buffer->meta()->findObject("accessUnitInfo", &obj)) {
+ ALOGV("Filling C2Info from multiple access units");
+ sp<WrapperObject<std::vector<AccessUnitInfo>>> infos{
+ (decltype(infos.get()))obj.get()};
+ std::vector<AccessUnitInfo> &accessUnitInfoVec = infos->value;
+ std::vector<C2AccessUnitInfosStruct> multipleAccessUnitInfos;
+ uint32_t outFlags = 0;
+ for (int i = 0; i < accessUnitInfoVec.size(); i++) {
+ outFlags = 0;
+ outFlags = convertFlags(accessUnitInfoVec[i].mFlags, true);
+ if (eos && (outFlags & C2FrameData::FLAG_END_OF_STREAM)) {
+ outFlags &= (~C2FrameData::FLAG_END_OF_STREAM);
+ }
+ multipleAccessUnitInfos.emplace_back(
+ outFlags,
+ accessUnitInfoVec[i].mSize,
+ accessUnitInfoVec[i].mTimestamp);
+ ALOGV("%d) flags: %d, size: %d, time: %llu",
+ i, outFlags, accessUnitInfoVec[i].mSize,
+ (long long)accessUnitInfoVec[i].mTimestamp);
+
+ }
+ const std::shared_ptr<C2AccessUnitInfos::input> c2AccessUnitInfos =
+ C2AccessUnitInfos::input::AllocShared(
+ multipleAccessUnitInfos.size(), 0u, multipleAccessUnitInfos);
+ c2buffer->setInfo(c2AccessUnitInfos);
+ }
work->input.buffers.push_back(c2buffer);
if (encryptedBlock) {
work->input.infoBuffers.emplace_back(C2InfoBuffer::CreateLinearBuffer(
@@ -1604,8 +1655,14 @@
padding = 0;
}
if (delay || padding) {
- // We need write access to the buffers, and we're already in
- // array mode.
+ // We need write access to the buffers, so turn them into array mode.
+ // TODO: b/321930152 - define SkipCutOutputBuffers that takes output from
+ // component, runs it through SkipCutBuffer and allocate local buffer to be
+ // used by fwk. Make initSkipCutBuffer() return OutputBuffers similar to
+ // toArrayMode().
+ if (!output->buffers->isArrayMode()) {
+ output->buffers = output->buffers->toArrayMode(numOutputSlots);
+ }
output->buffers->initSkipCutBuffer(delay, padding, sampleRate, channelCount);
}
}
@@ -2259,12 +2316,34 @@
case OutputBuffers::DISCARD:
break;
case OutputBuffers::NOTIFY_CLIENT:
+ {
// TRICKY: we want popped buffers reported in order, so sending
// the callback while holding the lock here. This assumes that
// onOutputBufferAvailable() does not block. onOutputBufferAvailable()
// callbacks are always sent with the Output lock held.
+ if (c2Buffer) {
+ std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
+ std::static_pointer_cast<const C2AccessUnitInfos::output>(
+ c2Buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
+ if (bufferMetadata && bufferMetadata->flexCount() > 0) {
+ uint32_t flag = 0;
+ std::vector<AccessUnitInfo> accessUnitInfos;
+ for (int nMeta = 0; nMeta < bufferMetadata->flexCount(); nMeta++) {
+ const C2AccessUnitInfosStruct &bufferMetadataStruct =
+ bufferMetadata->m.values[nMeta];
+ flag = convertFlags(bufferMetadataStruct.flags, false);
+ accessUnitInfos.emplace_back(flag,
+ static_cast<size_t>(bufferMetadataStruct.size),
+ static_cast<size_t>(bufferMetadataStruct.timestamp));
+ }
+ sp<WrapperObject<std::vector<AccessUnitInfo>>> obj{
+ new WrapperObject<std::vector<AccessUnitInfo>>{accessUnitInfos}};
+ outBuffer->meta()->setObject("accessUnitInfo", obj);
+ }
+ }
mCallback->onOutputBufferAvailable(index, outBuffer);
break;
+ }
case OutputBuffers::REALLOCATE:
if (++reallocTryNum > kMaxReallocTry) {
output.unlock();
diff --git a/media/codec2/sfplugin/CCodecBuffers.cpp b/media/codec2/sfplugin/CCodecBuffers.cpp
index 670923b..8a48777 100644
--- a/media/codec2/sfplugin/CCodecBuffers.cpp
+++ b/media/codec2/sfplugin/CCodecBuffers.cpp
@@ -18,11 +18,14 @@
#define LOG_TAG "CCodecBuffers"
#include <utils/Log.h>
+#include <numeric>
+
#include <C2AllocatorGralloc.h>
#include <C2PlatformSupport.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/MediaDefs.h>
+#include <media/stagefright/CodecBase.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <mediadrm/ICrypto.h>
@@ -147,6 +150,165 @@
return copy;
}
+// MultiAccessUnitSkipCutBuffer for buffer and bufferInfos
+
+class MultiAccessUnitSkipCutBuffer : public SkipCutBuffer {
+
+public:
+ explicit MultiAccessUnitSkipCutBuffer(
+ int32_t skip, int32_t cut, size_t num16BitChannels):
+ SkipCutBuffer(skip, cut, num16BitChannels),
+ mFrontPaddingDelay(0), mSize(0) {
+ }
+
+ virtual ~MultiAccessUnitSkipCutBuffer() {
+
+ }
+
+ void submitMultiAccessUnits(
+ const sp<MediaCodecBuffer>& buffer,
+ int32_t sampleRate, size_t num16BitChannels,
+ std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
+ if (infos == nullptr) {
+ // there is nothing to do more.
+ SkipCutBuffer::submit(buffer);
+ return;
+ }
+ typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+ CHECK_EQ(mSize, SkipCutBuffer::size());
+ sp<BufferInfosWrapper> bufferInfos{new BufferInfosWrapper(decltype(bufferInfos->value)())};
+ uint32_t availableSize = buffer->size() + SkipCutBuffer::size();
+ uint32_t frontPadding = mFrontPadding;
+ int32_t lastEmptyAccessUnitIndex = -1;
+ int64_t byteInUs = 0;
+ if (sampleRate > 0 && num16BitChannels > 0) {
+ byteInUs = (1000000u / (sampleRate * num16BitChannels * 2));
+ }
+ if (frontPadding > 0) {
+ mInfos.clear();
+ mSize = 0;
+ }
+ for (int i = 0 ; i < infos->flexCount() && frontPadding > 0; i++) {
+ uint32_t flagsInPadding = 0;
+ int64_t timeInPadding = 0;
+ if (infos->m.values[i].size <= frontPadding) {
+ // we have more front padding so this buffer is not going to be used.
+ int32_t consumed = infos->m.values[i].size;
+ frontPadding -= consumed;
+ mFrontPaddingDelay += byteInUs * (consumed);
+ availableSize -= consumed;
+ flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
+ timeInPadding = infos->m.values[i].timestamp;
+ } else {
+ C2AccessUnitInfosStruct info = infos->m.values[i];
+ mFrontPaddingDelay += byteInUs * (frontPadding);
+ info.size -= frontPadding;
+ info.timestamp -= mFrontPaddingDelay;
+ availableSize -= frontPadding;
+ flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
+ timeInPadding = infos->m.values[i].timestamp;
+ frontPadding = 0;
+ mInfos.push_back(info);
+ mSize += info.size;
+ }
+ if (flagsInPadding != 0) {
+ bufferInfos->value.emplace_back(
+ flagsInPadding, 0, timeInPadding);
+ }
+ lastEmptyAccessUnitIndex = i;
+ }
+ if (frontPadding <= 0) {
+ // process what's already in the buffer first
+ auto it = mInfos.begin();
+ while (it != mInfos.end() && availableSize > mBackPadding) {
+ // we have samples to send out.
+ if ((availableSize - it->size) >= mBackPadding) {
+ // this is totally used here.
+ int32_t consumed = it->size;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(it->flags), consumed, it->timestamp);
+ availableSize -= consumed;
+ mSize -= consumed;
+ it = mInfos.erase(it);
+ } else {
+ int32_t consumed = availableSize - mBackPadding;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(it->flags),
+ consumed,
+ it->timestamp);
+ it->size -= consumed;
+ it->timestamp += consumed * byteInUs;
+ availableSize -= consumed;
+ mSize -= consumed;
+ it++;
+ }
+ }
+ // if buffer has more process all of it and keep the remaining info.
+ for (int i = (lastEmptyAccessUnitIndex + 1) ; i < infos->flexCount() ; i++) {
+ // upddate updatedInfo and mInfos
+ if (availableSize > mBackPadding) {
+ // we have to take data from the new buffer.
+ if (availableSize - infos->m.values[i].size >= mBackPadding) {
+ // we are using this info
+ int32_t consumed = infos->m.values[i].size;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(infos->m.values[i].flags),
+ consumed,
+ infos->m.values[i].timestamp - mFrontPaddingDelay);
+ availableSize -= consumed;
+ } else {
+ // if we need to update the size
+ C2AccessUnitInfosStruct info = infos->m.values[i];
+ int32_t consumed = availableSize - mBackPadding;
+ bufferInfos->value.emplace_back(
+ toMediaCodecFlags(infos->m.values[i].flags),
+ consumed,
+ infos->m.values[i].timestamp - mFrontPaddingDelay);
+ info.size -= consumed;
+ info.timestamp = info.timestamp - mFrontPaddingDelay +
+ consumed * byteInUs;
+ mInfos.push_back(info);
+ availableSize -= consumed;
+ mSize += info.size;
+ }
+ } else {
+ // we have to maintain infos
+ C2AccessUnitInfosStruct info = infos->m.values[i];
+ info.timestamp -= mFrontPaddingDelay;
+ mInfos.push_back(info);
+ mSize += info.size;
+ }
+ }
+ }
+ SkipCutBuffer::submit(buffer);
+ infos = nullptr;
+ if (!bufferInfos->value.empty()) {
+ buffer->meta()->setObject("accessUnitInfo", bufferInfos);
+ }
+ }
+protected:
+ // Flags can come with individual BufferInfos
+ // when used with large frame audio
+ constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
+ {BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
+ {BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
+ {BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
+ };
+
+ static uint32_t toMediaCodecFlags(uint32_t flags) {
+ return std::transform_reduce(
+ flagList.begin(), flagList.end(),
+ 0u,
+ std::bit_or{},
+ [flags](const std::pair<uint32_t, uint32_t> &entry) {
+ return (flags & entry.second) ? entry.first : 0;
+ });
+ }
+ std::list<C2AccessUnitInfosStruct> mInfos;
+ int64_t mFrontPaddingDelay;
+ size_t mSize;
+};
+
// OutputBuffers
OutputBuffers::OutputBuffers(const char *componentName, const char *name)
@@ -201,6 +363,15 @@
}
}
+bool OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
+ int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
+ if (mSkipCutBuffer == nullptr) {
+ return false;
+ }
+ mSkipCutBuffer->submitMultiAccessUnits(buffer, sampleRate, channelCount, infos);
+ return true;
+}
+
void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut) {
if (mSkipCutBuffer != nullptr) {
size_t prevSize = mSkipCutBuffer->size();
@@ -208,7 +379,7 @@
ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
}
}
- mSkipCutBuffer = new SkipCutBuffer(skip, cut, mChannelCount);
+ mSkipCutBuffer = new MultiAccessUnitSkipCutBuffer(skip, cut, mChannelCount);
}
bool OutputBuffers::convert(
@@ -1160,7 +1331,16 @@
ALOGD("[%s] copy buffer failed", mName);
return WOULD_BLOCK;
}
- submit(c2Buffer);
+ if (buffer && buffer->hasInfo(C2AccessUnitInfos::output::PARAM_TYPE)) {
+ std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
+ std::static_pointer_cast<const C2AccessUnitInfos::output>(
+ buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
+ if (submit(c2Buffer, mSampleRate, mChannelCount, bufferMetadata)) {
+ buffer->removeInfo(C2AccessUnitInfos::output::PARAM_TYPE);
+ }
+ } else {
+ submit(c2Buffer);
+ }
handleImageData(c2Buffer);
*clientBuffer = c2Buffer;
ALOGV("[%s] grabbed buffer %zu", mName, *index);
diff --git a/media/codec2/sfplugin/CCodecBuffers.h b/media/codec2/sfplugin/CCodecBuffers.h
index cbef644..f0936bc 100644
--- a/media/codec2/sfplugin/CCodecBuffers.h
+++ b/media/codec2/sfplugin/CCodecBuffers.h
@@ -20,6 +20,7 @@
#include <optional>
#include <string>
+#include <vector>
#include <C2Config.h>
#include <DataConverter.h>
@@ -33,6 +34,8 @@
struct ICrypto;
class MemoryDealer;
class SkipCutBuffer;
+class MultiAccessUnitSkipCutBuffer;
+struct AccessUnitInfo;
constexpr size_t kLinearBufferSize = 1048576;
// This can fit an 8K frame.
@@ -382,13 +385,17 @@
sp<MediaCodecBuffer>* outBuffer);
protected:
- sp<SkipCutBuffer> mSkipCutBuffer;
+
+ sp<MultiAccessUnitSkipCutBuffer> mSkipCutBuffer;
/**
* Update the SkipCutBuffer object. No-op if it's never initialized.
*/
void updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount);
+ bool submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
+ int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos);
+
/**
* Submit buffer to SkipCutBuffer object, if initialized.
*/
diff --git a/media/codec2/sfplugin/CCodecConfig.cpp b/media/codec2/sfplugin/CCodecConfig.cpp
index 6d49fa8..c22deca 100644
--- a/media/codec2/sfplugin/CCodecConfig.cpp
+++ b/media/codec2/sfplugin/CCodecConfig.cpp
@@ -402,10 +402,19 @@
add(ConfigMapper(KEY_MAX_INPUT_SIZE, C2_PARAMKEY_INPUT_MAX_BUFFER_SIZE, "value")
.limitTo(D::INPUT));
+
// remove when codecs switch to PARAMKEY
deprecated(ConfigMapper(KEY_MAX_INPUT_SIZE, "coded.max-frame-size", "value")
.limitTo(D::INPUT));
+ // large frame params
+ add(ConfigMapper(KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE,
+ C2_PARAMKEY_OUTPUT_LARGE_FRAME, "max-size")
+ .limitTo(D::AUDIO & D::OUTPUT));
+ add(ConfigMapper(KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE,
+ C2_PARAMKEY_OUTPUT_LARGE_FRAME, "threshold-size")
+ .limitTo(D::AUDIO & D::OUTPUT));
+
// Rotation
// Note: SDK rotation is clock-wise, while C2 rotation is counter-clock-wise
add(ConfigMapper(KEY_ROTATION, C2_PARAMKEY_VUI_ROTATION, "value")
diff --git a/media/codec2/vndk/platform/C2IgbaBuffer.cpp b/media/codec2/vndk/platform/C2IgbaBuffer.cpp
index eafdb22..3622d5e 100644
--- a/media/codec2/vndk/platform/C2IgbaBuffer.cpp
+++ b/media/codec2/vndk/platform/C2IgbaBuffer.cpp
@@ -192,28 +192,25 @@
c2_status_t res = _fetchGraphicBlock(
width, height, format, usage, kBlockingFetchTimeoutNs, &origId, block, &fence);
- if (res == C2_BLOCKING) {
+ if (res == C2_TIMED_OUT) {
+ // SyncFence waiting timeout.
+ // Usually HAL treats C2_TIMED_OUT as an irrecoverable error.
+ // We want HAL to re-try.
return C2_BLOCKING;
}
- if (res != C2_OK) {
- return res;
- }
- // TODO: bundle the fence to the block. Are API changes required?
- res = fence.wait(kSyncFenceWaitNs);
- if (res != C2_OK) {
- bool aidlRet = true;
- ::ndk::ScopedAStatus status = mIgba->deallocate(origId, &aidlRet);
- ALOGE("Waiting a sync fence failed %d aidl(%d: %d)",
- res, status.isOk(), aidlRet);
- }
- return C2_OK;
+ return res;
}
c2_status_t C2IgbaBlockPool::fetchGraphicBlock(
uint32_t width, uint32_t height, uint32_t format, C2MemoryUsage usage,
std::shared_ptr<C2GraphicBlock> *block, C2Fence *fence) {
uint64_t origId;
- return _fetchGraphicBlock(width, height, format, usage, 0LL, &origId, block, fence);
+ c2_status_t res = _fetchGraphicBlock(width, height, format, usage, 0LL, &origId, block, fence);
+ if (res == C2_TIMED_OUT) {
+ *fence = C2Fence();
+ return C2_BLOCKING;
+ }
+ return res;
}
c2_status_t C2IgbaBlockPool::_fetchGraphicBlock(
@@ -263,10 +260,27 @@
}
}
- *fence = _C2FenceFactory::CreateSyncFence(allocation.fence.release());
+ C2Fence syncFence = _C2FenceFactory::CreateSyncFence(allocation.fence.release());
AHardwareBuffer *ahwb = allocation.buffer.release(); // This is acquired.
CHECK(AHardwareBuffer_getId(ahwb, origId) == ::android::OK);
- c2_status_t res = CreateGraphicBlockFromAhwb(ahwb, mAllocator, mIgba, block);
+
+ // We are waiting for SyncFence here for backward compatibility.
+ // H/W based Sync Fence could be returned to improve pipeline latency.
+ //
+ // TODO: Add a component configuration for returning sync fence
+ // from fetchGraphicBlock() as the C2Fence output param(b/322283520).
+ // In the case C2_OK along with GraphicBlock must be returned together.
+ c2_status_t res = syncFence.wait(kSyncFenceWaitNs);
+ if (res != C2_OK) {
+ AHardwareBuffer_release(ahwb);
+ bool aidlRet = true;
+ ::ndk::ScopedAStatus status = mIgba->deallocate(*origId, &aidlRet);
+ ALOGE("Waiting a sync fence failed %d aidl(%d: %d)",
+ res, status.isOk(), aidlRet);
+ return C2_TIMED_OUT;
+ }
+
+ res = CreateGraphicBlockFromAhwb(ahwb, mAllocator, mIgba, block);
AHardwareBuffer_release(ahwb);
if (res != C2_OK) {
bool aidlRet = true;
diff --git a/media/libaudioclient/TEST_MAPPING b/media/libaudioclient/TEST_MAPPING
index 234e858..68dba34 100644
--- a/media/libaudioclient/TEST_MAPPING
+++ b/media/libaudioclient/TEST_MAPPING
@@ -43,10 +43,9 @@
}
],
"postsubmit": [
- // TODO(b/302036943): Enable once we make it pass with AIDL HAL on CF.
- // {
- // "name": "audioeffect_analysis"
- // },
+ {
+ "name": "audioeffect_analysis"
+ },
{
"name": "CtsVirtualDevicesTestCases",
"options" : [
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
index 39999a5..e1a82f8 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.cpp
@@ -187,13 +187,7 @@
IEffect::OpenEffectReturn openReturn;
RETURN_STATUS_IF_ERROR(
statusTFromBinderStatus(mEffect->open(common, std::nullopt, &openReturn)));
- updateMqs(openReturn);
-
- if (status_t status = updateEventFlags(); status != OK) {
- ALOGV("%s closing at status %d", __func__, status);
- mEffect->close();
- return status;
- }
+ updateMqsAndEventFlags(openReturn);
} else if (mCommon != common) {
ALOGI("%s at state %s, setParameter", __func__, android::internal::ToString(state).c_str());
Parameter aidlParam = UNION_MAKE(Parameter, common, common);
@@ -204,13 +198,21 @@
return *static_cast<int32_t*>(pReplyData) = OK;
}
-void EffectConversionHelperAidl::updateMqs(const IEffect::OpenEffectReturn& ret) {
+void EffectConversionHelperAidl::updateMqsAndEventFlags(const IEffect::OpenEffectReturn& ret) {
if (mIsProxyEffect) {
mStatusQ = std::static_pointer_cast<EffectProxy>(mEffect)->getStatusMQ();
+ } else {
+ mStatusQ = std::make_shared<StatusMQ>(ret.statusMQ);
+ }
+ updateEventFlags();
+ updateDataMqs(ret);
+}
+
+void EffectConversionHelperAidl::updateDataMqs(const IEffect::OpenEffectReturn& ret) {
+ if (mIsProxyEffect) {
mInputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getInputMQ();
mOutputQ = std::static_pointer_cast<EffectProxy>(mEffect)->getOutputMQ();
} else {
- mStatusQ = std::make_shared<StatusMQ>(ret.statusMQ);
mInputQ = std::make_shared<DataMQ>(ret.inputDataMQ);
mOutputQ = std::make_shared<DataMQ>(ret.outputDataMQ);
}
@@ -407,10 +409,8 @@
}
// update FMQs if the effect instance already open
if (State state; effectProxy->getState(&state).isOk() && state != State::INIT) {
- mStatusQ = effectProxy->getStatusMQ();
- mInputQ = effectProxy->getInputMQ();
- mOutputQ = effectProxy->getOutputMQ();
- updateEventFlags();
+ IEffect::OpenEffectReturn openReturn;
+ updateMqsAndEventFlags(openReturn);
}
}
return *static_cast<int32_t*>(pReplyData) = OK;
@@ -512,7 +512,8 @@
IEffect::OpenEffectReturn openReturn;
RETURN_STATUS_IF_ERROR(statusTFromBinderStatus(mEffect->reopen(&openReturn)));
- updateMqs(openReturn);
+ // status MQ won't be changed after open
+ updateDataMqs(openReturn);
return OK;
}
diff --git a/media/libaudiohal/impl/EffectConversionHelperAidl.h b/media/libaudiohal/impl/EffectConversionHelperAidl.h
index 8b9efb3..c4841c5 100644
--- a/media/libaudiohal/impl/EffectConversionHelperAidl.h
+++ b/media/libaudiohal/impl/EffectConversionHelperAidl.h
@@ -98,7 +98,6 @@
std::shared_ptr<StatusMQ> mStatusQ = nullptr;
std::shared_ptr<DataMQ> mInputQ = nullptr, mOutputQ = nullptr;
-
struct EventFlagDeleter {
void operator()(::android::hardware::EventFlag* flag) const {
if (flag) {
@@ -108,8 +107,10 @@
};
std::shared_ptr<android::hardware::EventFlag> mEfGroup = nullptr;
status_t updateEventFlags();
-
- void updateMqs(const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
+ void updateDataMqs(
+ const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
+ void updateMqsAndEventFlags(
+ const ::aidl::android::hardware::audio::effect::IEffect::OpenEffectReturn& ret);
status_t handleInit(uint32_t cmdSize, const void* pCmdData, uint32_t* replySize,
void* pReplyData);
diff --git a/media/libeffects/downmix/aidl/EffectDownmix.cpp b/media/libeffects/downmix/aidl/EffectDownmix.cpp
index c82c23b..46156ce 100644
--- a/media/libeffects/downmix/aidl/EffectDownmix.cpp
+++ b/media/libeffects/downmix/aidl/EffectDownmix.cpp
@@ -195,6 +195,8 @@
const auto availableToWrite = outputMQ->availableToWrite() *
mImplContext->getInputFrameSize() /
mImplContext->getOutputFrameSize();
+ assert(mImplContext->getWorkBufferSize() >=
+ std::max(availableToRead(), availableToWrite));
auto processSamples = std::min(availableToRead, availableToWrite);
if (processSamples) {
inputMQ->read(buffer, processSamples);
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index bb7e4c6..b7a42cd 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -854,21 +854,34 @@
LOG(DEBUG) << "Effect_process() processing last frame";
}
mNumberEffectsCalled = 0;
- float* outTmp = (accumulate ? getWorkBuffer() : out);
- /* Process the samples */
- LVM_ReturnStatus_en lvmStatus;
- {
- std::lock_guard lg(mMutex);
-
- lvmStatus = LVM_Process(mInstance, in, outTmp, inputFrameCount, 0);
- if (lvmStatus != LVM_SUCCESS) {
- LOG(ERROR) << __func__ << lvmStatus;
- return {EX_UNSUPPORTED_OPERATION, 0, 0};
- }
- if (accumulate) {
- for (int i = 0; i < samples; i++) {
- out[i] += outTmp[i];
+ int frames = samples * sizeof(float) / frameSize;
+ int bufferIndex = 0;
+ // LVM library supports max of int16_t frames at a time and should be multiple of
+ // kBlockSizeMultiple.
+ constexpr int kBlockSizeMultiple = 4;
+ constexpr int kMaxBlockFrames =
+ (std::numeric_limits<int16_t>::max() / kBlockSizeMultiple) * kBlockSizeMultiple;
+ while (frames > 0) {
+ float* outTmp = (accumulate ? getWorkBuffer() : out);
+ /* Process the samples */
+ LVM_ReturnStatus_en lvmStatus;
+ {
+ std::lock_guard lg(mMutex);
+ int processFrames = std::min(frames, kMaxBlockFrames);
+ lvmStatus = LVM_Process(mInstance, in + bufferIndex, outTmp + bufferIndex,
+ processFrames, 0);
+ if (lvmStatus != LVM_SUCCESS) {
+ LOG(ERROR) << "LVM lib failed with error: " << lvmStatus;
+ return {EX_UNSUPPORTED_OPERATION, 0, 0};
}
+ if (accumulate) {
+ for (int i = 0; i < samples; i++) {
+ out[i] += outTmp[i];
+ }
+ }
+ frames -= processFrames;
+ int processedSize = processFrames * frameSize / sizeof(float);
+ bufferIndex += processedSize;
}
}
} else {
diff --git a/media/libeffects/lvm/wrapper/Android.bp b/media/libeffects/lvm/wrapper/Android.bp
index a50ba93..62837b9 100644
--- a/media/libeffects/lvm/wrapper/Android.bp
+++ b/media/libeffects/lvm/wrapper/Android.bp
@@ -120,7 +120,6 @@
shared_libs: [
"libaudio_aidl_conversion_common_ndk",
"libaudioutils",
- "libbinder",
"liblog",
"libstagefright_foundation",
],
diff --git a/media/libmediaplayerservice/fuzzer/Android.bp b/media/libmediaplayerservice/fuzzer/Android.bp
index b511372..507da29 100644
--- a/media/libmediaplayerservice/fuzzer/Android.bp
+++ b/media/libmediaplayerservice/fuzzer/Android.bp
@@ -124,14 +124,19 @@
],
defaults: [
"libmediaplayerserviceFuzzer_defaults",
+ "libmediaplayerservice_defaults",
],
static_libs: [
"libplayerservice_datasource",
],
shared_libs: [
+ "libmediaplayerservice",
"libdatasource",
"libdrmframework",
+ "libstagefright_httplive",
+ "libmediaextractorservice",
],
+ include_dirs: ["frameworks/av/services/mediaextractor"],
}
cc_fuzz {
diff --git a/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp b/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp
index a7cb689..857223d 100644
--- a/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp
+++ b/media/libmediaplayerservice/fuzzer/metadataretriever_fuzzer.cpp
@@ -15,6 +15,8 @@
*
*/
+#include <MediaExtractorService.h>
+#include <MediaPlayerService.h>
#include <StagefrightMetadataRetriever.h>
#include <binder/ProcessState.h>
#include <datasource/FileSource.h>
@@ -54,58 +56,96 @@
MEDIA_MIMETYPE_CONTAINER_MPEG2PS, MEDIA_MIMETYPE_CONTAINER_HEIF,
MEDIA_MIMETYPE_TEXT_3GPP, MEDIA_MIMETYPE_TEXT_SUBRIP,
MEDIA_MIMETYPE_TEXT_VTT, MEDIA_MIMETYPE_TEXT_CEA_608,
- MEDIA_MIMETYPE_TEXT_CEA_708, MEDIA_MIMETYPE_DATA_TIMED_ID3};
+ MEDIA_MIMETYPE_TEXT_CEA_708, MEDIA_MIMETYPE_DATA_TIMED_ID3,
+ MEDIA_MIMETYPE_IMAGE_AVIF, MEDIA_MIMETYPE_AUDIO_MPEGH_MHA1,
+ MEDIA_MIMETYPE_AUDIO_MPEGH_MHM1, MEDIA_MIMETYPE_AUDIO_MPEGH_BL_L3,
+ MEDIA_MIMETYPE_AUDIO_MPEGH_BL_L4, MEDIA_MIMETYPE_AUDIO_MPEGH_LC_L3,
+ MEDIA_MIMETYPE_AUDIO_MPEGH_LC_L4, MEDIA_MIMETYPE_AUDIO_DTS,
+ MEDIA_MIMETYPE_AUDIO_DTS_HD, MEDIA_MIMETYPE_AUDIO_DTS_HD_MA,
+ MEDIA_MIMETYPE_AUDIO_DTS_UHD, MEDIA_MIMETYPE_AUDIO_DTS_UHD_P1,
+ MEDIA_MIMETYPE_AUDIO_DTS_UHD_P2, MEDIA_MIMETYPE_AUDIO_EVRC,
+ MEDIA_MIMETYPE_AUDIO_EVRCB, MEDIA_MIMETYPE_AUDIO_EVRCWB,
+ MEDIA_MIMETYPE_AUDIO_EVRCNW, MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS,
+ MEDIA_MIMETYPE_AUDIO_APTX, MEDIA_MIMETYPE_AUDIO_DRA,
+ MEDIA_MIMETYPE_AUDIO_DOLBY_MAT, MEDIA_MIMETYPE_AUDIO_DOLBY_TRUEHD,
+ MEDIA_MIMETYPE_AUDIO_DOLBY_MAT_1_0,MEDIA_MIMETYPE_AUDIO_AAC_MP4,
+ MEDIA_MIMETYPE_AUDIO_DOLBY_MAT_2_0,MEDIA_MIMETYPE_AUDIO_DOLBY_MAT_2_1,
+ MEDIA_MIMETYPE_AUDIO_AAC_MAIN, MEDIA_MIMETYPE_AUDIO_AAC_LC,
+ MEDIA_MIMETYPE_AUDIO_AAC_SSR, MEDIA_MIMETYPE_AUDIO_AAC_LTP,
+ MEDIA_MIMETYPE_AUDIO_AAC_HE_V1, MEDIA_MIMETYPE_AUDIO_AAC_SCALABLE,
+ MEDIA_MIMETYPE_AUDIO_AAC_ERLC, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_MAIN,
+ MEDIA_MIMETYPE_AUDIO_AAC_HE_V2, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_HE_V1,
+ MEDIA_MIMETYPE_AUDIO_AAC_XHE, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_HE_V2,
+ MEDIA_MIMETYPE_AUDIO_AAC_LD, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LC,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_SSR, MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LTP,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADIF, MEDIA_MIMETYPE_AUDIO_IEC60958,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_ERLC,MEDIA_MIMETYPE_AUDIO_AAC_ADTS_LD,
+ MEDIA_MIMETYPE_AUDIO_AAC_ELD, MEDIA_MIMETYPE_AUDIO_AAC_LATM_HE_V1,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_XHE, MEDIA_MIMETYPE_AUDIO_AAC_LATM_LC,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_ELD, MEDIA_MIMETYPE_AUDIO_AAC_LATM_HE_V2,
+ MEDIA_MIMETYPE_AUDIO_IEC61937,
+ MEDIA_MIMETYPE_AUDIO_AAC_ADTS_SCALABLE,};
+
+constexpr size_t kMaxSize = 100;
class MetadataRetrieverFuzzer {
public:
MetadataRetrieverFuzzer(const uint8_t *data, size_t size)
- : mFdp(data, size),
- mMdRetriever(new StagefrightMetadataRetriever()),
- mDataSourceFd(memfd_create("InputFile", MFD_ALLOW_SEALING)) {}
- ~MetadataRetrieverFuzzer() { close(mDataSourceFd); }
+ : mFdp(data, size), mMdRetriever(new StagefrightMetadataRetriever()) {}
bool setDataSource(const uint8_t *data, size_t size);
void getData();
private:
FuzzedDataProvider mFdp;
sp<StagefrightMetadataRetriever> mMdRetriever = nullptr;
- const int32_t mDataSourceFd;
+ int32_t mDataSourceFd;
};
void MetadataRetrieverFuzzer::getData() {
- int64_t timeUs = mFdp.ConsumeIntegral<int64_t>();
- int32_t option = mFdp.ConsumeIntegral<int32_t>();
- int32_t colorFormat = mFdp.ConsumeIntegral<int32_t>();
- bool metaOnly = mFdp.ConsumeBool();
- mMdRetriever->getFrameAtTime(timeUs, option, colorFormat, metaOnly);
-
- int32_t index = mFdp.ConsumeIntegral<int32_t>();
- colorFormat = mFdp.ConsumeIntegral<int32_t>();
- metaOnly = mFdp.ConsumeBool();
- bool thumbnail = mFdp.ConsumeBool();
- mMdRetriever->getImageAtIndex(index, colorFormat, metaOnly, thumbnail);
-
- index = mFdp.ConsumeIntegral<int32_t>();
- colorFormat = mFdp.ConsumeIntegral<int32_t>();
- int32_t left = mFdp.ConsumeIntegral<int32_t>();
- int32_t top = mFdp.ConsumeIntegral<int32_t>();
- int32_t right = mFdp.ConsumeIntegral<int32_t>();
- int32_t bottom = mFdp.ConsumeIntegral<int32_t>();
- mMdRetriever->getImageRectAtIndex(index, colorFormat, left, top, right, bottom);
-
- index = mFdp.ConsumeIntegral<int32_t>();
- colorFormat = mFdp.ConsumeIntegral<int32_t>();
- metaOnly = mFdp.ConsumeBool();
- mMdRetriever->getFrameAtIndex(index, colorFormat, metaOnly);
-
- mMdRetriever->extractAlbumArt();
-
- int32_t keyCode = mFdp.ConsumeIntegral<int32_t>();
- mMdRetriever->extractMetadata(keyCode);
+ while (mFdp.remaining_bytes()) {
+ auto invokeMediaApi = mFdp.PickValueInArray<const std::function<void()>>({
+ [&]() {
+ mMdRetriever->getFrameAtTime(mFdp.ConsumeIntegral<int64_t>() /* timeUs */,
+ mFdp.ConsumeIntegral<int32_t>() /* option */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeBool() /* metaOnly */);
+ },
+ [&]() {
+ mMdRetriever->getImageAtIndex(mFdp.ConsumeIntegral<int32_t>() /* index */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeBool() /* metaOnly */,
+ mFdp.ConsumeBool() /* thumbnail */);
+ },
+ [&]() {
+ mMdRetriever->getImageRectAtIndex(
+ mFdp.ConsumeIntegral<int32_t>() /* index */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeIntegral<int32_t>() /* left */,
+ mFdp.ConsumeIntegral<int32_t>() /* top */,
+ mFdp.ConsumeIntegral<int32_t>() /* right */,
+ mFdp.ConsumeIntegral<int32_t>() /* bottom */);
+ },
+ [&]() {
+ mMdRetriever->getFrameAtIndex(mFdp.ConsumeIntegral<int32_t>() /* index */,
+ mFdp.ConsumeIntegral<int32_t>() /* colorFormat */,
+ mFdp.ConsumeBool() /* metaOnly */);
+ },
+ [&]() { mMdRetriever->extractAlbumArt(); },
+ [&]() {
+ mMdRetriever->extractMetadata(mFdp.ConsumeIntegral<int32_t>() /* keyCode */);
+ },
+ });
+ invokeMediaApi();
+ }
}
bool MetadataRetrieverFuzzer::setDataSource(const uint8_t *data, size_t size) {
status_t status = -1;
+ std::unique_ptr<std::FILE, decltype(&fclose)> fp(tmpfile(), &fclose);
+ mDataSourceFd = fileno(fp.get());
+ if (mDataSourceFd < 0) {
+ return false;
+ }
enum DataSourceChoice {FromHttp, FromFd, FromFileSource, kMaxValue = FromFileSource};
switch (mFdp.ConsumeEnum<DataSourceChoice>()) {
@@ -114,7 +154,7 @@
mHeaders.add(String8(mFdp.ConsumeRandomLengthString().c_str()),
String8(mFdp.ConsumeRandomLengthString().c_str()));
- uint32_t dataBlobSize = mFdp.ConsumeIntegralInRange<uint16_t>(0, size);
+ uint32_t dataBlobSize = mFdp.ConsumeIntegralInRange<uint16_t>(0, min(kMaxSize,size));
vector<uint8_t> uriSuffix = mFdp.ConsumeBytes<uint8_t>(dataBlobSize);
string uri("data:");
@@ -146,6 +186,12 @@
return true;
}
+extern "C" int LLVMFuzzerInitialize(int* /* argc */, char*** /* argv */) {
+ MediaPlayerService::instantiate();
+ MediaExtractorService::instantiate();
+ return 0;
+}
+
extern "C" int LLVMFuzzerTestOneInput(const uint8_t *data, size_t size) {
MetadataRetrieverFuzzer mrtFuzzer(data, size);
ProcessState::self()->startThreadPool();
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index 2145dd9..f9ceef2 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -2188,7 +2188,7 @@
int32_t colorFormat = OMX_COLOR_FormatUnused;
OMX_U32 flexibleEquivalent = OMX_COLOR_FormatUnused;
if (!outputFormat->findInt32("color-format", &colorFormat)) {
- ALOGE("ouptut port did not have a color format (wrong domain?)");
+ ALOGE("output port did not have a color format (wrong domain?)");
return BAD_VALUE;
}
ALOGD("[%s] Requested output format %#x and got %#x.",
diff --git a/media/libstagefright/Android.bp b/media/libstagefright/Android.bp
index 712b405..896e021 100644
--- a/media/libstagefright/Android.bp
+++ b/media/libstagefright/Android.bp
@@ -315,6 +315,7 @@
"libaudioclient_aidl_conversion",
"packagemanager_aidl-cpp",
"server_configurable_flags",
+ "aconfig_mediacodec_flags_c_lib",
],
static_libs: [
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index a0a2891..bc1a97c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -172,6 +172,7 @@
const char *getTrackType() const;
void resetInternal();
int64_t trackMetaDataSize();
+ bool isTimestampValid(int64_t timeUs);
private:
// A helper class to handle faster write box with table entries
@@ -2430,6 +2431,42 @@
return OK;
}
+bool MPEG4Writer::isSampleMetadataValid(size_t trackIndex, int64_t timeUs) {
+ // Track Index starts from zero, so it should be at least 1 less than size.
+ if (trackIndex >= mTracks.size()) {
+ ALOGE("Incorrect trackIndex %zu, mTracks->size() %zu", trackIndex, mTracks.size());
+ return false;
+ }
+
+ List<Track *>::iterator it = mTracks.begin();
+
+ // (*it) is already pointing to trackIndex 0.
+ for (int i = 1; i <= trackIndex; i++) {
+ it++;
+ }
+
+ return (*it)->isTimestampValid(timeUs);
+}
+
+bool MPEG4Writer::Track::isTimestampValid(int64_t timeUs) {
+ // No timescale if HEIF
+ if (mIsHeif) {
+ return true;
+ }
+
+ // Ensure that the timeUs value does not have extremely low or high values
+ // that would cause an underflow or overflow, like in the calculation -
+ // mdhdDuration = (trakDurationUs * mTimeScale + 5E5) / 1E6
+ if (abs(timeUs) >= (INT64_MAX - 5E5) / mTimeScale) {
+ return false;
+ }
+ // Limit check for calculations in ctts box
+ if (abs(timeUs) + kMaxCttsOffsetTimeUs >= INT64_MAX / mTimeScale) {
+ return false;
+ }
+ return true;
+}
+
bool MPEG4Writer::Track::isExifData(
MediaBufferBase *buffer, uint32_t *tiffHdrOffset) const {
if (!mIsHeif) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 6aac0e5..006eb90 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -273,6 +273,38 @@
// XXX suppress until we get our representation right
static bool kEmitHistogram = false;
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+
+// Multi access unit helpers
+static status_t generateFlagsFromAccessUnitInfo(
+ sp<AMessage> &msg, const sp<BufferInfosWrapper> &bufferInfos) {
+ msg->setInt64("timeUs", bufferInfos->value[0].mTimestamp);
+ msg->setInt32("flags", bufferInfos->value[0].mFlags);
+ // will prevent any access-unit info copy.
+ if (bufferInfos->value.size() > 1) {
+ uint32_t bufferFlags = 0;
+ uint32_t flagsInAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODEC_CONFIG;
+ uint32_t andFlags = flagsInAllAU;
+ int infoIdx = 0;
+ bool foundEndOfStream = false;
+ for ( ; infoIdx < bufferInfos->value.size() && !foundEndOfStream; ++infoIdx) {
+ bufferFlags |= bufferInfos->value[infoIdx].mFlags;
+ andFlags &= bufferInfos->value[infoIdx].mFlags;
+ if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+ foundEndOfStream = true;
+ }
+ }
+ bufferFlags = bufferFlags & (andFlags | (~flagsInAllAU));
+ if (infoIdx != bufferInfos->value.size()) {
+ ALOGE("Error: incorrect access-units");
+ return -EINVAL;
+ }
+ msg->setInt32("flags", bufferFlags);
+ msg->setObject("accessUnitInfo", bufferInfos);
+ }
+ return OK;
+}
+
static int64_t getId(IResourceManagerClient const * client) {
return (int64_t) client;
}
@@ -3176,7 +3208,49 @@
msg->setInt64("timeUs", presentationTimeUs);
msg->setInt32("flags", flags);
msg->setPointer("errorDetailMsg", errorDetailMsg);
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+status_t MediaCodec::queueInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<BufferInfosWrapper> &infos,
+ AString *errorDetailMsg) {
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
+ uint32_t bufferFlags = 0;
+ uint32_t flagsinAllAU = BUFFER_FLAG_DECODE_ONLY | BUFFER_FLAG_CODECCONFIG;
+ uint32_t andFlags = flagsinAllAU;
+ if (infos == nullptr || infos->value.empty()) {
+ ALOGE("ERROR: Large Audio frame with no BufferInfo");
+ return BAD_VALUE;
+ }
+ int infoIdx = 0;
+ std::vector<AccessUnitInfo> &accessUnitInfo = infos->value;
+ int64_t minTimeUs = accessUnitInfo.front().mTimestamp;
+ bool foundEndOfStream = false;
+ for ( ; infoIdx < accessUnitInfo.size() && !foundEndOfStream; ++infoIdx) {
+ bufferFlags |= accessUnitInfo[infoIdx].mFlags;
+ andFlags &= accessUnitInfo[infoIdx].mFlags;
+ if (bufferFlags & BUFFER_FLAG_END_OF_STREAM) {
+ foundEndOfStream = true;
+ }
+ }
+ bufferFlags = bufferFlags & (andFlags | (~flagsinAllAU));
+ if (infoIdx != accessUnitInfo.size()) {
+ ALOGE("queueInputBuffers has incorrect access-units");
+ return -EINVAL;
+ }
+ msg->setSize("index", index);
+ msg->setSize("offset", offset);
+ msg->setSize("size", size);
+ msg->setInt64("timeUs", minTimeUs);
+ // Make this represent flags for the entire buffer
+ // decodeOnly Flag is set only when all buffers are decodeOnly
+ msg->setInt32("flags", bufferFlags);
+ msg->setObject("accessUnitInfo", infos);
+ msg->setPointer("errorDetailMsg", errorDetailMsg);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
@@ -3220,28 +3294,30 @@
status_t MediaCodec::queueBuffer(
size_t index,
const std::shared_ptr<C2Buffer> &buffer,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
errorDetailMsg->clear();
}
-
+ if (bufferInfos == nullptr || bufferInfos->value.empty()) {
+ return BAD_VALUE;
+ }
+ status_t err = OK;
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->setSize("index", index);
sp<WrapperObject<std::shared_ptr<C2Buffer>>> obj{
new WrapperObject<std::shared_ptr<C2Buffer>>{buffer}};
msg->setObject("c2buffer", obj);
- msg->setInt64("timeUs", presentationTimeUs);
- msg->setInt32("flags", flags);
+ if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
+ return err;
+ }
if (tunings && tunings->countEntries() > 0) {
msg->setMessage("tunings", tunings);
}
msg->setPointer("errorDetailMsg", errorDetailMsg);
-
sp<AMessage> response;
- status_t err = PostAndAwaitResponse(msg, &response);
+ err = PostAndAwaitResponse(msg, &response);
return err;
}
@@ -3256,14 +3332,16 @@
const uint8_t iv[16],
CryptoPlugin::Mode mode,
const CryptoPlugin::Pattern &pattern,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
errorDetailMsg->clear();
}
-
+ if (bufferInfos == nullptr || bufferInfos->value.empty()) {
+ return BAD_VALUE;
+ }
+ status_t err = OK;
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->setSize("index", index);
sp<WrapperObject<sp<hardware::HidlMemory>>> memory{
@@ -3277,15 +3355,16 @@
msg->setInt32("mode", mode);
msg->setInt32("encryptBlocks", pattern.mEncryptBlocks);
msg->setInt32("skipBlocks", pattern.mSkipBlocks);
- msg->setInt64("timeUs", presentationTimeUs);
- msg->setInt32("flags", flags);
+ if (OK != (err = generateFlagsFromAccessUnitInfo(msg, bufferInfos))) {
+ return err;
+ }
if (tunings && tunings->countEntries() > 0) {
msg->setMessage("tunings", tunings);
}
msg->setPointer("errorDetailMsg", errorDetailMsg);
sp<AMessage> response;
- status_t err = PostAndAwaitResponse(msg, &response);
+ err = PostAndAwaitResponse(msg, &response);
return err;
}
@@ -4760,6 +4839,33 @@
}
}
}
+ int32_t largeFrameParam;
+ if (format->findInt32(KEY_BUFFER_BATCH_MAX_OUTPUT_SIZE, &largeFrameParam) ||
+ format->findInt32(KEY_BUFFER_BATCH_THRESHOLD_OUTPUT_SIZE,
+ &largeFrameParam)) {
+ if(mComponentName.startsWith("OMX")) {
+ mErrorLog.log(LOG_TAG,
+ "Large Frame params are not supported on OMX codecs."
+ "Currently only supported on C2 audio codec.");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ AString mime;
+ CHECK(format->findString("mime", &mime));
+ if (!mime.startsWith("audio")) {
+ mErrorLog.log(LOG_TAG,
+ "Large Frame params only works with audio codec");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ if (!(mFlags & kFlagIsAsync)) {
+ mErrorLog.log(LOG_TAG, "Large Frame audio" \
+ "config works only with async mode");
+ PostReplyWithError(replyID, INVALID_OPERATION);
+ break;
+ }
+ }
+
mReplyID = replyID;
setState(CONFIGURING);
@@ -5908,10 +6014,10 @@
status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
size_t index;
- size_t offset;
- size_t size;
- int64_t timeUs;
- uint32_t flags;
+ size_t offset = 0;
+ size_t size = 0;
+ int64_t timeUs = 0;
+ uint32_t flags = 0;
CHECK(msg->findSize("index", &index));
CHECK(msg->findInt64("timeUs", &timeUs));
CHECK(msg->findInt32("flags", (int32_t *)&flags));
@@ -5993,9 +6099,13 @@
"client does not own the buffer #%zu", index));
return -EACCES;
}
- auto setInputBufferParams = [this, &buffer]
+ auto setInputBufferParams = [this, &msg, &buffer]
(int64_t timeUs, uint32_t flags = 0) -> status_t {
status_t err = OK;
+ sp<RefBase> obj;
+ if (msg->findObject("accessUnitInfo", &obj)) {
+ buffer->meta()->setObject("accessUnitInfo", obj);
+ }
buffer->meta()->setInt64("timeUs", timeUs);
if (flags & BUFFER_FLAG_EOS) {
buffer->meta()->setInt32("eos", true);
@@ -6505,10 +6615,11 @@
if (discardDecodeOnlyOutputBuffer(index)) {
continue;
}
+ sp<AMessage> msg = mCallback->dup();
const sp<MediaCodecBuffer> &buffer =
mPortBuffers[kPortIndexOutput][index].mData;
- sp<AMessage> msg = mCallback->dup();
- msg->setInt32("callbackID", CB_OUTPUT_AVAILABLE);
+ int32_t outputCallbackID = CB_OUTPUT_AVAILABLE;
+ sp<RefBase> accessUnitInfoObj;
msg->setInt32("index", index);
msg->setSize("offset", buffer->offset());
msg->setSize("size", buffer->size());
@@ -6522,6 +6633,15 @@
CHECK(buffer->meta()->findInt32("flags", &flags));
msg->setInt32("flags", flags);
+ buffer->meta()->findObject("accessUnitInfo", &accessUnitInfoObj);
+ if (accessUnitInfoObj) {
+ outputCallbackID = CB_LARGE_FRAME_OUTPUT_AVAILABLE;
+ msg->setObject("accessUnitInfo", accessUnitInfoObj);
+ sp<BufferInfosWrapper> auInfo(
+ (decltype(auInfo.get()))accessUnitInfoObj.get());
+ auInfo->value.back().mFlags |= flags & BUFFER_FLAG_END_OF_STREAM;
+ }
+ msg->setInt32("callbackID", outputCallbackID);
statsBufferReceived(timeUs, buffer);
diff --git a/media/libstagefright/TEST_MAPPING b/media/libstagefright/TEST_MAPPING
index 22885c9..5dd8423 100644
--- a/media/libstagefright/TEST_MAPPING
+++ b/media/libstagefright/TEST_MAPPING
@@ -1,8 +1,5 @@
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
// writerTest fails about 5 out of 66
// { "name": "writerTest" },
diff --git a/media/libstagefright/include/media/stagefright/CodecBase.h b/media/libstagefright/include/media/stagefright/CodecBase.h
index 0927653..8741daa 100644
--- a/media/libstagefright/include/media/stagefright/CodecBase.h
+++ b/media/libstagefright/include/media/stagefright/CodecBase.h
@@ -61,6 +61,16 @@
using hardware::cas::native::V1_0::IDescrambler;
+struct AccessUnitInfo {
+ uint32_t mFlags;
+ uint32_t mSize;
+ int64_t mTimestamp;
+ AccessUnitInfo(uint32_t flags, uint32_t size, int64_t ptsUs)
+ :mFlags(flags), mSize(size), mTimestamp(ptsUs) {
+ }
+ ~AccessUnitInfo() {}
+};
+
struct CodecParameterDescriptor {
std::string name;
AMessage::Type type;
diff --git a/media/libstagefright/include/media/stagefright/MPEG4Writer.h b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
index 054a4b8..1ff8acf 100644
--- a/media/libstagefright/include/media/stagefright/MPEG4Writer.h
+++ b/media/libstagefright/include/media/stagefright/MPEG4Writer.h
@@ -77,6 +77,9 @@
virtual void setStartTimeOffsetMs(int ms) { mStartTimeOffsetMs = ms; }
virtual int32_t getStartTimeOffsetMs() const { return mStartTimeOffsetMs; }
virtual status_t setNextFd(int fd);
+ // Returns true if the timestamp is valid which is compatible with the Mpeg4.
+ // Note that this overloads that method in the base class.
+ bool isSampleMetadataValid(size_t trackIndex, int64_t timeUs) override;
protected:
virtual ~MPEG4Writer();
diff --git a/media/libstagefright/include/media/stagefright/MediaCodec.h b/media/libstagefright/include/media/stagefright/MediaCodec.h
index 2f94e5e..6622f4f 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodec.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodec.h
@@ -28,6 +28,7 @@
#include <media/MediaMetrics.h>
#include <media/MediaProfiles.h>
#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/CodecErrorLog.h>
#include <media/stagefright/FrameRenderTracker.h>
#include <media/stagefright/MediaHistogram.h>
@@ -56,6 +57,7 @@
struct AString;
struct BatteryChecker;
class BufferChannelBase;
+struct AccessUnitInfo;
struct CodecBase;
struct CodecParameterDescriptor;
class IBatteryStats;
@@ -78,6 +80,8 @@
using aidl::android::media::MediaResourceParcel;
using aidl::android::media::ClientConfigParcel;
+typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
+
struct MediaCodec : public AHandler {
enum Domain {
DOMAIN_UNKNOWN = 0,
@@ -115,6 +119,7 @@
CB_OUTPUT_FORMAT_CHANGED = 4,
CB_RESOURCE_RECLAIMED = 5,
CB_CRYPTO_ERROR = 6,
+ CB_LARGE_FRAME_OUTPUT_AVAILABLE = 7,
};
static const pid_t kNoPid = -1;
@@ -185,6 +190,13 @@
uint32_t flags,
AString *errorDetailMsg = NULL);
+ status_t queueInputBuffers(
+ size_t index,
+ size_t offset,
+ size_t size,
+ const sp<BufferInfosWrapper> &accessUnitInfo,
+ AString *errorDetailMsg = NULL);
+
status_t queueSecureInputBuffer(
size_t index,
size_t offset,
@@ -201,8 +213,7 @@
status_t queueBuffer(
size_t index,
const std::shared_ptr<C2Buffer> &buffer,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg = NULL);
@@ -216,8 +227,7 @@
const uint8_t iv[16],
CryptoPlugin::Mode mode,
const CryptoPlugin::Pattern &pattern,
- int64_t presentationTimeUs,
- uint32_t flags,
+ const sp<BufferInfosWrapper> &bufferInfos,
const sp<AMessage> &tunings,
AString *errorDetailMsg = NULL);
diff --git a/media/libstagefright/include/media/stagefright/SkipCutBuffer.h b/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
index 0fb5690..66f0bb1 100644
--- a/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
+++ b/media/libstagefright/include/media/stagefright/SkipCutBuffer.h
@@ -59,6 +59,12 @@
int32_t mReadHead;
int32_t mCapacity;
char* mCutBuffer;
+
+ /*
+ * Added to use Access unit skip cut in Codec2 framework
+ */
+ friend class MultiAccessUnitSkipCutBuffer;
+
DISALLOW_EVIL_CONSTRUCTORS(SkipCutBuffer);
};
diff --git a/media/libstagefright/timedtext/TEST_MAPPING b/media/libstagefright/timedtext/TEST_MAPPING
index 35a5b11..f011d04 100644
--- a/media/libstagefright/timedtext/TEST_MAPPING
+++ b/media/libstagefright/timedtext/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/libstagefright/timedtext
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
{ "name": "TimedTextUnitTest" }
]
}
diff --git a/media/module/codecs/amrnb/TEST_MAPPING b/media/module/codecs/amrnb/TEST_MAPPING
index 343d08a..306921f 100644
--- a/media/module/codecs/amrnb/TEST_MAPPING
+++ b/media/module/codecs/amrnb/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/libstagefright/codecs/amrnb
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
{ "name": "AmrnbDecoderTest"},
{ "name": "AmrnbEncoderTest"}
]
diff --git a/media/module/codecs/amrwb/TEST_MAPPING b/media/module/codecs/amrwb/TEST_MAPPING
new file mode 100644
index 0000000..3f05c90
--- /dev/null
+++ b/media/module/codecs/amrwb/TEST_MAPPING
@@ -0,0 +1,6 @@
+{
+ "postsubmit": [
+ { "name": "AmrwbDecoderTest"},
+ { "name": "AmrwbEncoderTest"}
+ ]
+}
diff --git a/media/module/codecs/amrwb/dec/TEST_MAPPING b/media/module/codecs/amrwb/dec/TEST_MAPPING
deleted file mode 100644
index 0278d26..0000000
--- a/media/module/codecs/amrwb/dec/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-// mappings for frameworks/av/media/libstagefright/codecs/amrwb
-{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
- { "name": "AmrwbDecoderTest"}
-
- ]
-}
diff --git a/media/module/codecs/amrwb/enc/TEST_MAPPING b/media/module/codecs/amrwb/enc/TEST_MAPPING
deleted file mode 100644
index 045e8b3..0000000
--- a/media/module/codecs/amrwb/enc/TEST_MAPPING
+++ /dev/null
@@ -1,10 +0,0 @@
-// mappings for frameworks/av/media/libstagefright/codecs/amrwbenc
-{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
- { "name": "AmrwbEncoderTest"}
-
- ]
-}
diff --git a/media/module/codecs/flac/TEST_MAPPING b/media/module/codecs/flac/TEST_MAPPING
new file mode 100644
index 0000000..725ea90
--- /dev/null
+++ b/media/module/codecs/flac/TEST_MAPPING
@@ -0,0 +1,5 @@
+{
+ "postsubmit": [
+ { "name": "FlacDecoderTest"}
+ ]
+}
diff --git a/media/module/codecs/flac/dec/test/Android.bp b/media/module/codecs/flac/dec/test/Android.bp
index a4c2735..8004c4a 100644
--- a/media/module/codecs/flac/dec/test/Android.bp
+++ b/media/module/codecs/flac/dec/test/Android.bp
@@ -28,6 +28,7 @@
cc_test {
name: "FlacDecoderTest",
gtest: true,
+ test_suites: ["device-tests"],
srcs: [
"FlacDecoderTest.cpp",
diff --git a/media/module/codecs/m4v_h263/TEST_MAPPING b/media/module/codecs/m4v_h263/TEST_MAPPING
index ba3ff1c..8599fa5 100644
--- a/media/module/codecs/m4v_h263/TEST_MAPPING
+++ b/media/module/codecs/m4v_h263/TEST_MAPPING
@@ -1,18 +1,6 @@
-// mappings for frameworks/av/media/libstagefright/codecs/m4v_h263
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
-
- // the decoder reports something bad about an unexpected newline in the *config file
- // and the config file looks like the AndroidTest.xml file that we put in there.
- // I don't get this from the Encoder -- and I don't see any substantive difference
- // between decode and encode AndroidTest.xml files -- except that encode does NOT
- // finish with a newline.
- // strange.
+ "postsubmit": [
{ "name": "Mpeg4H263DecoderTest"},
{ "name": "Mpeg4H263EncoderTest"}
-
]
}
diff --git a/media/module/codecs/mp3dec/TEST_MAPPING b/media/module/codecs/mp3dec/TEST_MAPPING
index 4ef4317..5faece6 100644
--- a/media/module/codecs/mp3dec/TEST_MAPPING
+++ b/media/module/codecs/mp3dec/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/libstagefright/codecs/mp3dec
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
{ "name": "Mp3DecoderTest"}
]
}
diff --git a/media/module/esds/TEST_MAPPING b/media/module/esds/TEST_MAPPING
index 9368b6d..0337743 100644
--- a/media/module/esds/TEST_MAPPING
+++ b/media/module/esds/TEST_MAPPING
@@ -1,9 +1,5 @@
-// mappings for frameworks/av/media/module/esds
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
{ "name": "ESDSTest" }
]
}
diff --git a/media/module/esds/tests/Android.bp b/media/module/esds/tests/Android.bp
index aea611e..427b275 100644
--- a/media/module/esds/tests/Android.bp
+++ b/media/module/esds/tests/Android.bp
@@ -25,6 +25,7 @@
cc_test {
name: "ESDSTest",
gtest: true,
+ test_suites: ["device-tests"],
srcs: [
"ESDSTest.cpp",
diff --git a/media/module/foundation/TEST_MAPPING b/media/module/foundation/TEST_MAPPING
index a70c352..ea4e4fd 100644
--- a/media/module/foundation/TEST_MAPPING
+++ b/media/module/foundation/TEST_MAPPING
@@ -1,9 +1,6 @@
-// mappings for frameworks/av/media/libstagefright/foundation
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
+ { "name": "AVCUtilsUnitTest" },
{ "name": "OpusHeaderTest" }
],
diff --git a/media/module/foundation/include/media/stagefright/foundation/AMessage.h b/media/module/foundation/include/media/stagefright/foundation/AMessage.h
index 7594565..b301c53 100644
--- a/media/module/foundation/include/media/stagefright/foundation/AMessage.h
+++ b/media/module/foundation/include/media/stagefright/foundation/AMessage.h
@@ -356,6 +356,16 @@
DISALLOW_EVIL_CONSTRUCTORS(AMessage);
};
+/*
+ * Helper struct for wrapping any object with RefBase.
+ */
+template <typename T>
+struct WrapperObject : public RefBase {
+ WrapperObject(const T& v) : value(v) {}
+ WrapperObject(T&& v) : value(std::move(v)) {}
+ T value;
+};
+
} // namespace android
#endif // A_MESSAGE_H_
diff --git a/media/module/foundation/tests/AVCUtils/Android.bp b/media/module/foundation/tests/AVCUtils/Android.bp
index ee7db21..c306c73 100644
--- a/media/module/foundation/tests/AVCUtils/Android.bp
+++ b/media/module/foundation/tests/AVCUtils/Android.bp
@@ -28,6 +28,7 @@
cc_test {
name: "AVCUtilsUnitTest",
gtest: true,
+ test_suites: ["device-tests"],
srcs: [
"AVCUtilsUnitTest.cpp",
diff --git a/media/module/foundation/tests/AVCUtils/AndroidTest.xml b/media/module/foundation/tests/AVCUtils/AndroidTest.xml
index e30bfbf..315373f 100644
--- a/media/module/foundation/tests/AVCUtils/AndroidTest.xml
+++ b/media/module/foundation/tests/AVCUtils/AndroidTest.xml
@@ -16,7 +16,7 @@
<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="cleanup" value="true" />
<option name="push" value="AVCUtilsUnitTest->/data/local/tmp/AVCUtilsUnitTest" />
</target_preparer>
diff --git a/media/module/id3/TEST_MAPPING b/media/module/id3/TEST_MAPPING
index 6106908..497d984 100644
--- a/media/module/id3/TEST_MAPPING
+++ b/media/module/id3/TEST_MAPPING
@@ -1,9 +1,5 @@
-// frameworks/av/media/libstagefright/id3
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
{ "name": "ID3Test" }
],
diff --git a/media/module/mpeg2ts/TEST_MAPPING b/media/module/mpeg2ts/TEST_MAPPING
index 9f4bbdf..536450f 100644
--- a/media/module/mpeg2ts/TEST_MAPPING
+++ b/media/module/mpeg2ts/TEST_MAPPING
@@ -1,9 +1,5 @@
-// frameworks/av/media/libstagefright/mpeg2ts
{
- // tests which require dynamic content
- // invoke with: atest -- --enable-module-dynamic-download=true
- // TODO(b/148094059): unit tests not allowed to download content
- "dynamic-presubmit": [
+ "postsubmit": [
{ "name": "Mpeg2tsUnitTest" }
]
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 2fd908f..0057c9b 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -3265,6 +3265,7 @@
// requested device or one of the devices selected by the engine for this stream
// - For default requested device (AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME), apply volume only if
// no specific device volume value exists for currently selected device.
+ // - Only apply the volume if the requested device is the desired device for volume control.
for (size_t i = 0; i < mOutputs.size(); i++) {
sp<SwAudioOutputDescriptor> desc = mOutputs.valueAt(i);
DeviceTypeSet curDevices = desc->devices().types();
@@ -3284,7 +3285,8 @@
if (device != AUDIO_DEVICE_OUT_DEFAULT_FOR_VOLUME) {
curSrcDevices.insert(device);
applyVolume = (curSrcDevices.find(
- Volume::getDeviceForVolume(curDevices)) != curSrcDevices.end());
+ Volume::getDeviceForVolume(curDevices)) != curSrcDevices.end())
+ && Volume::getDeviceForVolume(curSrcDevices) == device;
} else {
applyVolume = !curves.hasVolumeIndexForDevice(curSrcDevice);
}
diff --git a/services/mediaresourcemanager/DefaultResourceModel.cpp b/services/mediaresourcemanager/DefaultResourceModel.cpp
index 7bad715..990df82 100644
--- a/services/mediaresourcemanager/DefaultResourceModel.cpp
+++ b/services/mediaresourcemanager/DefaultResourceModel.cpp
@@ -44,7 +44,9 @@
clients.clear();
MediaResourceParcel mediaResource{.type = reclimRequestInfo.mResources[0].type,
.subType = reclimRequestInfo.mResources[0].subType};
- ResourceRequestInfo resourceRequestInfo{reclimRequestInfo.mCallingPid, &mediaResource};
+ ResourceRequestInfo resourceRequestInfo{reclimRequestInfo.mCallingPid,
+ reclimRequestInfo.mClientId,
+ &mediaResource};
// Resolve the secure-unsecure codec conflicts if there is any.
switch (reclimRequestInfo.mResources[0].type) {
@@ -116,7 +118,9 @@
const ReclaimRequestInfo& reclimRequestInfo,
std::vector<ClientInfo>& clients) {
MediaResourceParcel mediaResource;
- ResourceRequestInfo resourceRequestInfo{reclimRequestInfo.mCallingPid, &mediaResource};
+ ResourceRequestInfo resourceRequestInfo{reclimRequestInfo.mCallingPid,
+ reclimRequestInfo.mClientId,
+ &mediaResource};
// 1. Look to find the client(s) with the other resources, for the given
// primary type.
diff --git a/services/mediaresourcemanager/ResourceManagerService.cpp b/services/mediaresourcemanager/ResourceManagerService.cpp
index 3a02443..305f6fe 100644
--- a/services/mediaresourcemanager/ResourceManagerService.cpp
+++ b/services/mediaresourcemanager/ResourceManagerService.cpp
@@ -474,6 +474,7 @@
const std::vector<MediaResourceParcel>& resources,
std::vector<ClientInfo>& targetClients) {
int32_t callingPid = clientInfo.pid;
+ int64_t clientId = clientInfo.id;
std::scoped_lock lock{mLock};
if (!mProcessInfo->isPidTrusted(callingPid)) {
pid_t actualCallingPid = IPCThreadState::self()->getCallingPid();
@@ -508,7 +509,7 @@
if (secureCodec != NULL) {
MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec,
.subType = secureCodec->subType};
- ResourceRequestInfo resourceRequestInfo{callingPid, &mediaResource};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &mediaResource};
if (!mSupportsMultipleSecureCodecs) {
if (!getAllClients_l(resourceRequestInfo, targetClients)) {
return false;
@@ -525,7 +526,7 @@
if (!mSupportsSecureWithNonSecureCodec) {
MediaResourceParcel mediaResource{.type = MediaResource::Type::kSecureCodec,
.subType = nonSecureCodec->subType};
- ResourceRequestInfo resourceRequestInfo{callingPid, &mediaResource};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &mediaResource};
if (!getAllClients_l(resourceRequestInfo, targetClients)) {
return false;
}
@@ -533,7 +534,7 @@
}
if (drmSession != NULL) {
- ResourceRequestInfo resourceRequestInfo{callingPid, drmSession};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, drmSession};
getClientForResource_l(resourceRequestInfo, targetClients);
if (targetClients.size() == 0) {
return false;
@@ -542,18 +543,18 @@
if (targetClients.size() == 0 && graphicMemory != nullptr) {
// if no secure/non-secure codec conflict, run second pass to handle other resources.
- ResourceRequestInfo resourceRequestInfo{callingPid, graphicMemory};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, graphicMemory};
getClientForResource_l(resourceRequestInfo, targetClients);
}
if (targetClients.size() == 0) {
// if we are here, run the third pass to free one codec with the same type.
if (secureCodec != nullptr) {
- ResourceRequestInfo resourceRequestInfo{callingPid, secureCodec};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, secureCodec};
getClientForResource_l(resourceRequestInfo, targetClients);
}
if (nonSecureCodec != nullptr) {
- ResourceRequestInfo resourceRequestInfo{callingPid, nonSecureCodec};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, nonSecureCodec};
getClientForResource_l(resourceRequestInfo, targetClients);
}
}
@@ -562,12 +563,12 @@
// if we are here, run the fourth pass to free one codec with the different type.
if (secureCodec != nullptr) {
MediaResource temp(MediaResource::Type::kNonSecureCodec, secureCodec->subType, 1);
- ResourceRequestInfo resourceRequestInfo{callingPid, &temp};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &temp};
getClientForResource_l(resourceRequestInfo, targetClients);
}
if (nonSecureCodec != nullptr) {
MediaResource temp(MediaResource::Type::kSecureCodec, nonSecureCodec->subType, 1);
- ResourceRequestInfo resourceRequestInfo{callingPid, &temp};
+ ResourceRequestInfo resourceRequestInfo{callingPid, clientId, &temp};
getClientForResource_l(resourceRequestInfo, targetClients);
}
}
@@ -914,6 +915,11 @@
for (auto& [pid, infos] : mMap) {
for (const auto& [id, info] : infos) {
+ if (pid == resourceRequestInfo.mCallingPid && id == resourceRequestInfo.mClientId) {
+ ALOGI("%s: Skip the client[%jd] for which the resource request is made",
+ __func__, id);
+ continue;
+ }
if (hasResourceType(type, subType, info.resources)) {
if (!isCallingPriorityHigher_l(resourceRequestInfo.mCallingPid, pid)) {
// some higher/equal priority process owns the resource,
diff --git a/services/mediaresourcemanager/ResourceManagerServiceNew.cpp b/services/mediaresourcemanager/ResourceManagerServiceNew.cpp
index af093ca..0a0a8f4 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceNew.cpp
+++ b/services/mediaresourcemanager/ResourceManagerServiceNew.cpp
@@ -248,7 +248,7 @@
// Use the Resource Model to get a list of all the clients that hold the
// needed/requested resources.
uint32_t callingImportance = std::max(0, clientInfo.importance);
- ReclaimRequestInfo reclaimRequestInfo{callingPid, callingImportance, resources};
+ ReclaimRequestInfo reclaimRequestInfo{callingPid, clientInfo.id, callingImportance, resources};
std::vector<ClientInfo> clients;
if (!mDefaultResourceModel->getAllClients(reclaimRequestInfo, clients)) {
if (clients.empty()) {
@@ -300,7 +300,10 @@
// Use the DefaultResourceModel to get all the clients with the resources requested.
std::vector<MediaResourceParcel> resources{*resourceRequestInfo.mResource};
- ReclaimRequestInfo reclaimRequestInfo{resourceRequestInfo.mCallingPid, 0, resources};
+ ReclaimRequestInfo reclaimRequestInfo{resourceRequestInfo.mCallingPid,
+ resourceRequestInfo.mClientId,
+ 0, // default importance
+ resources};
std::vector<ClientInfo> clients;
mDefaultResourceModel->getAllClients(reclaimRequestInfo, clients);
diff --git a/services/mediaresourcemanager/ResourceManagerServiceUtils.h b/services/mediaresourcemanager/ResourceManagerServiceUtils.h
index 32cb219..e8f1515 100644
--- a/services/mediaresourcemanager/ResourceManagerServiceUtils.h
+++ b/services/mediaresourcemanager/ResourceManagerServiceUtils.h
@@ -166,11 +166,13 @@
/*
* Resource Reclaim request info that encapsulates
* - the calling/requesting process pid.
+ * - id of the client that made reclaim request.
* - the calling/requesting client's importance.
* - the list of resources requesting (to be reclaimed from others)
*/
struct ReclaimRequestInfo {
int mCallingPid = -1;
+ int64_t mClientId = 0;
uint32_t mCallingClientImportance = 0;
const std::vector<::aidl::android::media::MediaResourceParcel>& mResources;
};
@@ -178,11 +180,14 @@
/*
* Resource request info that encapsulates
* - the calling/requesting process pid.
+ * - the calling/requesting client's id.
* - the resource requesting (to be reclaimed from others)
*/
struct ResourceRequestInfo {
// pid of the calling/requesting process.
int mCallingPid = -1;
+ // id of the calling/requesting client.
+ int64_t mClientId = 0;
// resources requested.
const ::aidl::android::media::MediaResourceParcel* mResource;
};
diff --git a/services/mediaresourcemanager/ResourceTracker.cpp b/services/mediaresourcemanager/ResourceTracker.cpp
index 22381c3..3ee20cd 100644
--- a/services/mediaresourcemanager/ResourceTracker.cpp
+++ b/services/mediaresourcemanager/ResourceTracker.cpp
@@ -715,6 +715,11 @@
MediaResource::SubType subType = resourceRequestInfo.mResource->subType;
for (auto& [pid, /* ResourceInfos */ infos] : mMap) {
for (const auto& [id, /* ResourceInfo */ info] : infos) {
+ if (pid == resourceRequestInfo.mCallingPid && id == resourceRequestInfo.mClientId) {
+ ALOGI("%s: Skip the client[%jd] for which the resource request is made",
+ __func__, id);
+ continue;
+ }
if (hasResourceType(type, subType, info.resources)) {
if (!isCallingPriorityHigher(resourceRequestInfo.mCallingPid, pid)) {
// some higher/equal priority process owns the resource,
diff --git a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
index 7e8a4a0..2c8659d 100644
--- a/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
+++ b/services/mediaresourcemanager/test/ResourceManagerServiceTestUtils.h
@@ -165,6 +165,7 @@
DISALLOW_EVIL_CONSTRUCTORS(TestClient);
};
+// [pid, uid] used by the test.
static const int kTestPid1 = 30;
static const int kTestUid1 = 1010;
@@ -175,6 +176,12 @@
static const int kMidPriorityPid = 25;
static const int kHighPriorityPid = 10;
+// Client Ids used by the test.
+static const int kLowPriorityClientId = 1111;
+static const int kMidPriorityClientId = 2222;
+static const int kHighPriorityClientId = 3333;
+
+// Client importance used by the test.
static const int32_t kHighestCodecImportance = 0;
static const int32_t kLowestCodecImportance = 100;
static const int32_t kMidCodecImportance = 50;
diff --git a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
index b3a0932..027987e 100644
--- a/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
+++ b/services/mediaresourcemanager/test/ResourceManagerService_test.cpp
@@ -337,7 +337,7 @@
// priority too low to reclaim resource
ClientInfoParcel clientInfo{.pid = static_cast<int32_t>(kLowPriorityPid),
.uid = static_cast<int32_t>(kTestUid1),
- .id = 0,
+ .id = kLowPriorityClientId,
.name = "none"};
CHECK_STATUS_FALSE(mService->reclaimResource(clientInfo, resources, &result));
@@ -475,9 +475,9 @@
MediaResource resource(MediaResource::Type::kSecureCodec,
MediaResource::SubType::kUnspecifiedSubType,
1);
- ResourceRequestInfo requestInfoHigh { kHighPriorityPid, &resource};
- ResourceRequestInfo requestInfoMid { kMidPriorityPid, &resource};
- ResourceRequestInfo requestInfoLow { kLowPriorityPid, &resource};
+ ResourceRequestInfo requestInfoHigh { kHighPriorityPid, kHighPriorityClientId, &resource};
+ ResourceRequestInfo requestInfoMid { kMidPriorityPid, kMidPriorityClientId, &resource};
+ ResourceRequestInfo requestInfoLow { kLowPriorityPid, kLowPriorityClientId, &resource};
EXPECT_FALSE(mService->getAllClients_l(requestInfoLow, targetClients));
// some higher priority process (e.g. kTestPid2) owns the resource, so getAllClients_l
@@ -491,6 +491,81 @@
EXPECT_EQ(getId(mTestClient1), targetClients[1].mClientId);
}
+ // test set up
+ // ---------------------------------------------------------------------------
+ // pid/priority client/clientId type number
+ // ---------------------------------------------------------------------------
+ // kTestPid1(30) mTestClient1 secure codec 1
+ // graphic memory 200
+ // graphic memory 200
+ // ---------------------------------------------------------------------------
+ // kTestPid2(20) mTestClient2 non-secure codec 1
+ // graphic memory 300
+ // ---------------------------------------------------
+ // mTestClient3 secure codec 1
+ // graphic memory 100
+ // ---------------------------------------------------------------------------
+ // kHighPriorityPid(10) kHighPriorityClient secure codec 1
+ // ---------------------------------------------------------------------------
+ // The kHighPriorityClient tries to reclaim request (after adding self)
+ // This should pass (and shouldn't be considered as a new client trying to
+ // reclaim from an existing client from same/higher priority process).
+ void testSelfReclaimResourceSecure() {
+ std::vector<MediaResourceParcel> resources;
+ resources.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
+ resources.push_back(MediaResource(MediaResource::Type::kGraphicMemory, 150));
+
+ ClientInfoParcel lowPriorityClient{.pid = static_cast<int32_t>(kLowPriorityPid),
+ .uid = static_cast<int32_t>(kTestUid2),
+ .id = kLowPriorityClientId,
+ .name = "none"};
+ ClientInfoParcel midPriorityClient{.pid = static_cast<int32_t>(kMidPriorityPid),
+ .uid = static_cast<int32_t>(kTestUid2),
+ .id = kMidPriorityClientId,
+ .name = "none"};
+ // HighPriority process with client id kHighPriorityClientId.
+ ClientInfoParcel highPriorityClient1{.pid = static_cast<int32_t>(kHighPriorityPid),
+ .uid = static_cast<int32_t>(kTestUid2),
+ .id = kHighPriorityClientId,
+ .name = "none"};
+ // HighPriority process with client id 0xABCD.
+ ClientInfoParcel highPriorityClient2{.pid = static_cast<int32_t>(kHighPriorityPid),
+ .uid = static_cast<int32_t>(kTestUid2),
+ .id = 0xABCD,
+ .name = "none"};
+
+ addResource();
+
+ // Add a secure codec resource for the highPriorityClient1.
+ std::shared_ptr<IResourceManagerClient> testClient4 =
+ createTestClient(kHighPriorityPid, kTestUid2);
+ std::vector<MediaResourceParcel> resources1;
+ resources1.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
+ mService->addResource(highPriorityClient1, testClient4, resources1);
+
+ // secure codecs can't coexist and secure codec can't coexist with non-secure codec.
+ updateConfig(false, false);
+
+ // priority too low
+ CHECK_STATUS_FALSE(mService->reclaimResource(lowPriorityClient, resources, &result));
+ CHECK_STATUS_FALSE(mService->reclaimResource(midPriorityClient, resources, &result));
+
+ // highPriorityClient2 tries to reclaim SecureCodec with Graphic memory.
+ // This should fail as this process already has an instance of secure
+ // codec through testClient4.
+ CHECK_STATUS_FALSE(mService->reclaimResource(highPriorityClient2, resources, &result));
+
+ // highPriorityClient1 tries to reclaim SecureCodec with Graphic memory.
+ // This should reclaim all secure and non-secure codecs.
+ CHECK_STATUS_TRUE(mService->reclaimResource(highPriorityClient1, resources, &result));
+ EXPECT_TRUE(toTestClient(mTestClient1)->checkIfReclaimedAndReset());
+ EXPECT_TRUE(toTestClient(mTestClient2)->checkIfReclaimedAndReset());
+ EXPECT_TRUE(toTestClient(mTestClient3)->checkIfReclaimedAndReset());
+
+ // Make sure there is nothing left.
+ CHECK_STATUS_FALSE(mService->reclaimResource(highPriorityClient1, resources, &result));
+ }
+
void testReclaimResourceSecure() {
std::vector<MediaResourceParcel> resources;
resources.push_back(MediaResource(MediaResource::Type::kSecureCodec, 1));
@@ -498,15 +573,15 @@
ClientInfoParcel lowPriorityClient{.pid = static_cast<int32_t>(kLowPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kLowPriorityClientId,
.name = "none"};
ClientInfoParcel midPriorityClient{.pid = static_cast<int32_t>(kMidPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kMidPriorityClientId,
.name = "none"};
ClientInfoParcel highPriorityClient{.pid = static_cast<int32_t>(kHighPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kHighPriorityClientId,
.name = "none"};
// ### secure codec can't coexist and secure codec can coexist with non-secure codec ###
@@ -553,7 +628,6 @@
CHECK_STATUS_FALSE(mService->reclaimResource(highPriorityClient, resources, &result));
}
-
// ### secure codecs can coexist but secure codec can't coexist with non-secure codec ###
{
addResource();
@@ -650,15 +724,15 @@
ClientInfoParcel lowPriorityClient{.pid = static_cast<int32_t>(kLowPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kLowPriorityClientId,
.name = "none"};
ClientInfoParcel midPriorityClient{.pid = static_cast<int32_t>(kMidPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kMidPriorityClientId,
.name = "none"};
ClientInfoParcel highPriorityClient{.pid = static_cast<int32_t>(kHighPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kHighPriorityClientId,
.name = "none"};
// ### secure codec can't coexist with non-secure codec ###
@@ -751,8 +825,8 @@
MediaResource resource(MediaResource::Type::kGraphicMemory,
MediaResource::SubType::kUnspecifiedSubType,
1);
- ResourceRequestInfo requestInfoHigh { kHighPriorityPid, &resource};
- ResourceRequestInfo requestInfoLow { kLowPriorityPid, &resource};
+ ResourceRequestInfo requestInfoHigh { kHighPriorityPid, kHighPriorityClientId, &resource};
+ ResourceRequestInfo requestInfoLow { kLowPriorityPid, kLowPriorityClientId, &resource};
EXPECT_FALSE(mService->getLowestPriorityBiggestClient_l(requestInfoHigh, clientInfo));
addResource();
@@ -910,7 +984,7 @@
reclaimResources.push_back(createNonSecureVideoCodecResource());
ClientInfoParcel highPriorityClient{.pid = static_cast<int32_t>(kHighPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kHighPriorityClientId,
.name = "none"};
CHECK_STATUS_FALSE(mService->reclaimResource(highPriorityClient, reclaimResources, &result));
@@ -951,7 +1025,7 @@
reclaimResources.push_back(createNonSecureAudioCodecResource());
ClientInfoParcel highPriorityClient{.pid = static_cast<int32_t>(kHighPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kHighPriorityClientId,
.name = "none"};
CHECK_STATUS_FALSE(mService->reclaimResource(highPriorityClient, reclaimResources, &result));
@@ -992,7 +1066,7 @@
reclaimResources.push_back(createNonSecureImageCodecResource());
ClientInfoParcel highPriorityClient{.pid = static_cast<int32_t>(kHighPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kHighPriorityClientId,
.name = "none"};
CHECK_STATUS_FALSE(mService->reclaimResource(highPriorityClient, reclaimResources, &result));
@@ -1034,7 +1108,7 @@
reclaimResources.push_back(createGraphicMemoryResource(100));
ClientInfoParcel highPriorityClient{.pid = static_cast<int32_t>(kHighPriorityPid),
.uid = static_cast<int32_t>(kTestUid2),
- .id = 0,
+ .id = kHighPriorityClientId,
.name = "none"};
CHECK_STATUS_TRUE(mService->reclaimResource(highPriorityClient, reclaimResources, &result));
@@ -1786,6 +1860,10 @@
testRemoveClient();
}
+TEST_F(ResourceManagerServiceTest, selfReclaimResource) {
+ testSelfReclaimResourceSecure();
+}
+
TEST_F(ResourceManagerServiceTest, reclaimResource) {
testReclaimResourceSecure();
testReclaimResourceNonSecure();
@@ -1873,6 +1951,10 @@
testRemoveClient();
}
+TEST_F(ResourceManagerServiceNewTest, selfReclaimResource) {
+ testSelfReclaimResourceSecure();
+}
+
TEST_F(ResourceManagerServiceNewTest, reclaimResource) {
testReclaimResourceSecure();
testReclaimResourceNonSecure();