Merge "Prevent out-of-bounds read"
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.cpp b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
index 7486d27..617769b 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.cpp
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.cpp
@@ -31,6 +31,255 @@
namespace android {
+C2SoftVpxEnc::IntfImpl::IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
+ : SimpleInterface<void>::BaseParams(
+ helper,
+ COMPONENT_NAME,
+ C2Component::KIND_ENCODER,
+ C2Component::DOMAIN_VIDEO,
+ MEDIA_MIMETYPE_VIDEO) {
+ noPrivateBuffers(); // TODO: account for our buffers here
+ noInputReferences();
+ noOutputReferences();
+ noInputLatency();
+ noTimeStretch();
+ setDerivedInstance(this);
+
+ addParameter(
+ DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
+ .withConstValue(new C2ComponentAttributesSetting(
+ C2Component::ATTRIB_IS_TEMPORAL))
+ .build());
+
+ addParameter(
+ DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
+ .withConstValue(new C2StreamUsageTuning::input(
+ 0u, (uint64_t)C2MemoryUsage::CPU_READ))
+ .build());
+
+ addParameter(
+ DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
+ .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
+ .withFields({
+ C2F(mSize, width).inRange(2, 2048, 2),
+ C2F(mSize, height).inRange(2, 2048, 2),
+ })
+ .withSetter(SizeSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
+ .withDefault(new C2StreamBitrateModeTuning::output(
+ 0u, C2Config::BITRATE_VARIABLE))
+ .withFields({
+ C2F(mBitrateMode, value).oneOf({
+ C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE })
+ })
+ .withSetter(
+ Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
+ .build());
+
+ addParameter(
+ DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
+ .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
+ // TODO: More restriction?
+ .withFields({C2F(mFrameRate, value).greaterThan(0.)})
+ .withSetter(
+ Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
+ .build());
+
+ addParameter(
+ DefineParam(mLayering, C2_PARAMKEY_TEMPORAL_LAYERING)
+ .withDefault(C2StreamTemporalLayeringTuning::output::AllocShared(0u, 0, 0, 0))
+ .withFields({
+ C2F(mLayering, m.layerCount).inRange(0, 4),
+ C2F(mLayering, m.bLayerCount).inRange(0, 0),
+ C2F(mLayering, m.bitrateRatios).inRange(0., 1.)
+ })
+ .withSetter(LayeringSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
+ .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
+ .withFields({C2F(mSyncFramePeriod, value).any()})
+ .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
+ .build());
+
+ addParameter(
+ DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
+ .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
+ .withFields({C2F(mBitrate, value).inRange(4096, 40000000)})
+ .withSetter(BitrateSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
+ .withConstValue(new C2StreamIntraRefreshTuning::output(
+ 0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
+ .build());
+#ifdef VP9
+ addParameter(
+ DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+ .withDefault(new C2StreamProfileLevelInfo::output(
+ 0u, PROFILE_VP9_0, LEVEL_VP9_4_1))
+ .withFields({
+ C2F(mProfileLevel, profile).equalTo(
+ PROFILE_VP9_0
+ ),
+ C2F(mProfileLevel, level).equalTo(
+ LEVEL_VP9_4_1),
+ })
+ .withSetter(ProfileLevelSetter)
+ .build());
+#else
+ addParameter(
+ DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
+ .withDefault(new C2StreamProfileLevelInfo::output(
+ 0u, PROFILE_VP8_0, LEVEL_UNUSED))
+ .withFields({
+ C2F(mProfileLevel, profile).equalTo(
+ PROFILE_VP8_0
+ ),
+ C2F(mProfileLevel, level).equalTo(
+ LEVEL_UNUSED),
+ })
+ .withSetter(ProfileLevelSetter)
+ .build());
+#endif
+ addParameter(
+ DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
+ .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
+ .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
+ .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
+ .build());
+
+ addParameter(
+ DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::input(
+ 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({
+ C2F(mColorAspects, range).inRange(
+ C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+ C2F(mColorAspects, primaries).inRange(
+ C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+ C2F(mColorAspects, transfer).inRange(
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
+ C2F(mColorAspects, matrix).inRange(
+ C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
+ })
+ .withSetter(ColorAspectsSetter)
+ .build());
+
+ addParameter(
+ DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
+ .withDefault(new C2StreamColorAspectsInfo::output(
+ 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
+ .withFields({
+ C2F(mCodedColorAspects, range).inRange(
+ C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
+ C2F(mCodedColorAspects, primaries).inRange(
+ C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
+ C2F(mCodedColorAspects, transfer).inRange(
+ C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
+ C2F(mCodedColorAspects, matrix).inRange(
+ C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
+ })
+ .withSetter(CodedColorAspectsSetter, mColorAspects)
+ .build());
+}
+
+C2R C2SoftVpxEnc::IntfImpl::BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (me.v.value < 4096) {
+ me.set().value = 4096;
+ }
+ return res;
+}
+
+C2R C2SoftVpxEnc::IntfImpl::SizeSetter(bool mayBlock,
+ const C2P<C2StreamPictureSizeInfo::input>& oldMe,
+ C2P<C2StreamPictureSizeInfo::input>& me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
+ me.set().width = oldMe.v.width;
+ }
+ if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
+ res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
+ me.set().height = oldMe.v.height;
+ }
+ return res;
+}
+
+C2R C2SoftVpxEnc::IntfImpl::ProfileLevelSetter(bool mayBlock,
+ C2P<C2StreamProfileLevelInfo::output>& me) {
+ (void)mayBlock;
+ if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
+ me.set().profile = PROFILE_VP9_0;
+ }
+ if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
+ me.set().level = LEVEL_VP9_4_1;
+ }
+ return C2R::Ok();
+}
+
+C2R C2SoftVpxEnc::IntfImpl::LayeringSetter(bool mayBlock,
+ C2P<C2StreamTemporalLayeringTuning::output>& me) {
+ (void)mayBlock;
+ C2R res = C2R::Ok();
+ if (me.v.m.layerCount > 4) {
+ me.set().m.layerCount = 4;
+ }
+ me.set().m.bLayerCount = 0;
+ // ensure ratios are monotonic and clamped between 0 and 1
+ for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
+ me.set().m.bitrateRatios[ix] = c2_clamp(
+ ix > 0 ? me.v.m.bitrateRatios[ix - 1] : 0, me.v.m.bitrateRatios[ix], 1.);
+ }
+ ALOGI("setting temporal layering %u + %u", me.v.m.layerCount, me.v.m.bLayerCount);
+ return res;
+}
+
+uint32_t C2SoftVpxEnc::IntfImpl::getSyncFramePeriod() const {
+ if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
+ return 0;
+ }
+ double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
+ return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+}
+C2R C2SoftVpxEnc::IntfImpl::ColorAspectsSetter(bool mayBlock,
+ C2P<C2StreamColorAspectsInfo::input>& me) {
+ (void)mayBlock;
+ if (me.v.range > C2Color::RANGE_OTHER) {
+ me.set().range = C2Color::RANGE_OTHER;
+ }
+ if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
+ me.set().primaries = C2Color::PRIMARIES_OTHER;
+ }
+ if (me.v.transfer > C2Color::TRANSFER_OTHER) {
+ me.set().transfer = C2Color::TRANSFER_OTHER;
+ }
+ if (me.v.matrix > C2Color::MATRIX_OTHER) {
+ me.set().matrix = C2Color::MATRIX_OTHER;
+ }
+ return C2R::Ok();
+}
+C2R C2SoftVpxEnc::IntfImpl::CodedColorAspectsSetter(
+ bool mayBlock, C2P<C2StreamColorAspectsInfo::output>& me,
+ const C2P<C2StreamColorAspectsInfo::input>& coded) {
+ (void)mayBlock;
+ me.set().range = coded.v.range;
+ me.set().primaries = coded.v.primaries;
+ me.set().transfer = coded.v.transfer;
+ me.set().matrix = coded.v.matrix;
+ return C2R::Ok();
+}
+
#if 0
static size_t getCpuCoreCount() {
long cpuCoreCount = 1;
diff --git a/media/codec2/components/vpx/C2SoftVpxEnc.h b/media/codec2/components/vpx/C2SoftVpxEnc.h
index 926b2e9..e296c8f 100644
--- a/media/codec2/components/vpx/C2SoftVpxEnc.h
+++ b/media/codec2/components/vpx/C2SoftVpxEnc.h
@@ -237,259 +237,38 @@
class C2SoftVpxEnc::IntfImpl : public SimpleInterface<void>::BaseParams {
public:
- explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper)
- : SimpleInterface<void>::BaseParams(
- helper,
- COMPONENT_NAME,
- C2Component::KIND_ENCODER,
- C2Component::DOMAIN_VIDEO,
- MEDIA_MIMETYPE_VIDEO) {
- noPrivateBuffers(); // TODO: account for our buffers here
- noInputReferences();
- noOutputReferences();
- noInputLatency();
- noTimeStretch();
- setDerivedInstance(this);
-
- addParameter(
- DefineParam(mAttrib, C2_PARAMKEY_COMPONENT_ATTRIBUTES)
- .withConstValue(new C2ComponentAttributesSetting(
- C2Component::ATTRIB_IS_TEMPORAL))
- .build());
-
- addParameter(
- DefineParam(mUsage, C2_PARAMKEY_INPUT_STREAM_USAGE)
- .withConstValue(new C2StreamUsageTuning::input(
- 0u, (uint64_t)C2MemoryUsage::CPU_READ))
- .build());
-
- addParameter(
- DefineParam(mSize, C2_PARAMKEY_PICTURE_SIZE)
- .withDefault(new C2StreamPictureSizeInfo::input(0u, 320, 240))
- .withFields({
- C2F(mSize, width).inRange(2, 2048, 2),
- C2F(mSize, height).inRange(2, 2048, 2),
- })
- .withSetter(SizeSetter)
- .build());
-
- addParameter(
- DefineParam(mBitrateMode, C2_PARAMKEY_BITRATE_MODE)
- .withDefault(new C2StreamBitrateModeTuning::output(
- 0u, C2Config::BITRATE_VARIABLE))
- .withFields({
- C2F(mBitrateMode, value).oneOf({
- C2Config::BITRATE_CONST, C2Config::BITRATE_VARIABLE })
- })
- .withSetter(
- Setter<decltype(*mBitrateMode)>::StrictValueWithNoDeps)
- .build());
-
- addParameter(
- DefineParam(mFrameRate, C2_PARAMKEY_FRAME_RATE)
- .withDefault(new C2StreamFrameRateInfo::output(0u, 30.))
- // TODO: More restriction?
- .withFields({C2F(mFrameRate, value).greaterThan(0.)})
- .withSetter(
- Setter<decltype(*mFrameRate)>::StrictValueWithNoDeps)
- .build());
-
- addParameter(
- DefineParam(mLayering, C2_PARAMKEY_TEMPORAL_LAYERING)
- .withDefault(C2StreamTemporalLayeringTuning::output::AllocShared(0u, 0, 0, 0))
- .withFields({
- C2F(mLayering, m.layerCount).inRange(0, 4),
- C2F(mLayering, m.bLayerCount).inRange(0, 0),
- C2F(mLayering, m.bitrateRatios).inRange(0., 1.)
- })
- .withSetter(LayeringSetter)
- .build());
-
- addParameter(
- DefineParam(mSyncFramePeriod, C2_PARAMKEY_SYNC_FRAME_INTERVAL)
- .withDefault(new C2StreamSyncFrameIntervalTuning::output(0u, 1000000))
- .withFields({C2F(mSyncFramePeriod, value).any()})
- .withSetter(Setter<decltype(*mSyncFramePeriod)>::StrictValueWithNoDeps)
- .build());
-
- addParameter(
- DefineParam(mBitrate, C2_PARAMKEY_BITRATE)
- .withDefault(new C2StreamBitrateInfo::output(0u, 64000))
- .withFields({C2F(mBitrate, value).inRange(4096, 40000000)})
- .withSetter(BitrateSetter)
- .build());
-
- addParameter(
- DefineParam(mIntraRefresh, C2_PARAMKEY_INTRA_REFRESH)
- .withConstValue(new C2StreamIntraRefreshTuning::output(
- 0u, C2Config::INTRA_REFRESH_DISABLED, 0.))
- .build());
-#ifdef VP9
- addParameter(
- DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
- .withDefault(new C2StreamProfileLevelInfo::output(
- 0u, PROFILE_VP9_0, LEVEL_VP9_4_1))
- .withFields({
- C2F(mProfileLevel, profile).equalTo(
- PROFILE_VP9_0
- ),
- C2F(mProfileLevel, level).equalTo(
- LEVEL_VP9_4_1),
- })
- .withSetter(ProfileLevelSetter)
- .build());
-#else
- addParameter(
- DefineParam(mProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
- .withDefault(new C2StreamProfileLevelInfo::output(
- 0u, PROFILE_VP8_0, LEVEL_UNUSED))
- .withFields({
- C2F(mProfileLevel, profile).equalTo(
- PROFILE_VP8_0
- ),
- C2F(mProfileLevel, level).equalTo(
- LEVEL_UNUSED),
- })
- .withSetter(ProfileLevelSetter)
- .build());
-#endif
- addParameter(
- DefineParam(mRequestSync, C2_PARAMKEY_REQUEST_SYNC_FRAME)
- .withDefault(new C2StreamRequestSyncFrameTuning::output(0u, C2_FALSE))
- .withFields({C2F(mRequestSync, value).oneOf({ C2_FALSE, C2_TRUE }) })
- .withSetter(Setter<decltype(*mRequestSync)>::NonStrictValueWithNoDeps)
- .build());
-
- addParameter(
- DefineParam(mColorAspects, C2_PARAMKEY_COLOR_ASPECTS)
- .withDefault(new C2StreamColorAspectsInfo::input(
- 0u, C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED,
- C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
- .withFields({
- C2F(mColorAspects, range).inRange(
- C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
- C2F(mColorAspects, primaries).inRange(
- C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
- C2F(mColorAspects, transfer).inRange(
- C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
- C2F(mColorAspects, matrix).inRange(
- C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
- })
- .withSetter(ColorAspectsSetter)
- .build());
-
- addParameter(
- DefineParam(mCodedColorAspects, C2_PARAMKEY_VUI_COLOR_ASPECTS)
- .withDefault(new C2StreamColorAspectsInfo::output(
- 0u, C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED,
- C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED))
- .withFields({
- C2F(mCodedColorAspects, range).inRange(
- C2Color::RANGE_UNSPECIFIED, C2Color::RANGE_OTHER),
- C2F(mCodedColorAspects, primaries).inRange(
- C2Color::PRIMARIES_UNSPECIFIED, C2Color::PRIMARIES_OTHER),
- C2F(mCodedColorAspects, transfer).inRange(
- C2Color::TRANSFER_UNSPECIFIED, C2Color::TRANSFER_OTHER),
- C2F(mCodedColorAspects, matrix).inRange(
- C2Color::MATRIX_UNSPECIFIED, C2Color::MATRIX_OTHER)
- })
- .withSetter(CodedColorAspectsSetter, mColorAspects)
- .build());
- }
-
- static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me) {
- (void)mayBlock;
- C2R res = C2R::Ok();
- if (me.v.value <= 4096) {
- me.set().value = 4096;
- }
- return res;
- }
+ explicit IntfImpl(const std::shared_ptr<C2ReflectorHelper> &helper);
+ static C2R BitrateSetter(bool mayBlock, C2P<C2StreamBitrateInfo::output> &me);
static C2R SizeSetter(bool mayBlock, const C2P<C2StreamPictureSizeInfo::input> &oldMe,
- C2P<C2StreamPictureSizeInfo::input> &me) {
- (void)mayBlock;
- C2R res = C2R::Ok();
- if (!me.F(me.v.width).supportsAtAll(me.v.width)) {
- res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.width)));
- me.set().width = oldMe.v.width;
- }
- if (!me.F(me.v.height).supportsAtAll(me.v.height)) {
- res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.height)));
- me.set().height = oldMe.v.height;
- }
- return res;
- }
+ C2P<C2StreamPictureSizeInfo::input> &me);
static C2R ProfileLevelSetter(
bool mayBlock,
- C2P<C2StreamProfileLevelInfo::output> &me) {
- (void)mayBlock;
- if (!me.F(me.v.profile).supportsAtAll(me.v.profile)) {
- me.set().profile = PROFILE_VP9_0;
- }
- if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
- me.set().level = LEVEL_VP9_4_1;
- }
- return C2R::Ok();
- }
+ C2P<C2StreamProfileLevelInfo::output> &me);
- static C2R LayeringSetter(bool mayBlock, C2P<C2StreamTemporalLayeringTuning::output>& me) {
- (void)mayBlock;
- C2R res = C2R::Ok();
- if (me.v.m.layerCount > 4) {
- me.set().m.layerCount = 4;
- }
- me.set().m.bLayerCount = 0;
- // ensure ratios are monotonic and clamped between 0 and 1
- for (size_t ix = 0; ix < me.v.flexCount(); ++ix) {
- me.set().m.bitrateRatios[ix] = c2_clamp(
- ix > 0 ? me.v.m.bitrateRatios[ix - 1] : 0, me.v.m.bitrateRatios[ix], 1.);
- }
- ALOGI("setting temporal layering %u + %u", me.v.m.layerCount, me.v.m.bLayerCount);
- return res;
- }
+ static C2R LayeringSetter(bool mayBlock, C2P<C2StreamTemporalLayeringTuning::output>& me);
// unsafe getters
std::shared_ptr<C2StreamPictureSizeInfo::input> getSize_l() const { return mSize; }
- std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const { return mIntraRefresh; }
+ std::shared_ptr<C2StreamIntraRefreshTuning::output> getIntraRefresh_l() const {
+ return mIntraRefresh;
+ }
std::shared_ptr<C2StreamFrameRateInfo::output> getFrameRate_l() const { return mFrameRate; }
std::shared_ptr<C2StreamBitrateInfo::output> getBitrate_l() const { return mBitrate; }
- std::shared_ptr<C2StreamBitrateModeTuning::output> getBitrateMode_l() const { return mBitrateMode; }
- std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const { return mRequestSync; }
- std::shared_ptr<C2StreamTemporalLayeringTuning::output> getTemporalLayers_l() const { return mLayering; }
- uint32_t getSyncFramePeriod() const {
- if (mSyncFramePeriod->value < 0 || mSyncFramePeriod->value == INT64_MAX) {
- return 0;
- }
- double period = mSyncFramePeriod->value / 1e6 * mFrameRate->value;
- return (uint32_t)c2_max(c2_min(period + 0.5, double(UINT32_MAX)), 1.);
+ std::shared_ptr<C2StreamBitrateModeTuning::output> getBitrateMode_l() const {
+ return mBitrateMode;
}
- static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me) {
- (void)mayBlock;
- if (me.v.range > C2Color::RANGE_OTHER) {
- me.set().range = C2Color::RANGE_OTHER;
- }
- if (me.v.primaries > C2Color::PRIMARIES_OTHER) {
- me.set().primaries = C2Color::PRIMARIES_OTHER;
- }
- if (me.v.transfer > C2Color::TRANSFER_OTHER) {
- me.set().transfer = C2Color::TRANSFER_OTHER;
- }
- if (me.v.matrix > C2Color::MATRIX_OTHER) {
- me.set().matrix = C2Color::MATRIX_OTHER;
- }
- return C2R::Ok();
+ std::shared_ptr<C2StreamRequestSyncFrameTuning::output> getRequestSync_l() const {
+ return mRequestSync;
}
+ std::shared_ptr<C2StreamTemporalLayeringTuning::output> getTemporalLayers_l() const {
+ return mLayering;
+ }
+ uint32_t getSyncFramePeriod() const;
+ static C2R ColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::input> &me);
static C2R CodedColorAspectsSetter(bool mayBlock, C2P<C2StreamColorAspectsInfo::output> &me,
- const C2P<C2StreamColorAspectsInfo::input> &coded) {
- (void)mayBlock;
- me.set().range = coded.v.range;
- me.set().primaries = coded.v.primaries;
- me.set().transfer = coded.v.transfer;
- me.set().matrix = coded.v.matrix;
- return C2R::Ok();
- }
+ const C2P<C2StreamColorAspectsInfo::input> &coded);
private:
std::shared_ptr<C2StreamUsageTuning::input> mUsage;
diff --git a/media/codec2/sfplugin/Codec2InfoBuilder.cpp b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
index 7c4bfb6..67d7ed2 100644
--- a/media/codec2/sfplugin/Codec2InfoBuilder.cpp
+++ b/media/codec2/sfplugin/Codec2InfoBuilder.cpp
@@ -96,9 +96,12 @@
return false;
}
- // determine if codec supports HDR
+ // determine if codec supports HDR; imply 10-bit support
bool supportsHdr = false;
+ // determine if codec supports HDR10Plus; imply 10-bit support
bool supportsHdr10Plus = false;
+ // determine if codec supports 10-bit format
+ bool supports10Bit = false;
std::vector<std::shared_ptr<C2ParamDescriptor>> paramDescs;
c2_status_t err1 = intf->querySupportedParams(¶mDescs);
@@ -126,6 +129,10 @@
supportsHdr |= (mediaType == MIMETYPE_VIDEO_VP9);
supportsHdr |= (mediaType == MIMETYPE_VIDEO_AV1);
+ // HDR support implies 10-bit support.
+ // TODO: directly check this from the component interface
+ supports10Bit = (supportsHdr || supportsHdr10Plus);
+
bool added = false;
for (C2Value::Primitive profile : profileQuery[0].values.values) {
@@ -165,6 +172,12 @@
}
}
}
+ if (supports10Bit) {
+ auto bitnessMapper = C2Mapper::GetBitDepthProfileLevelMapper(trait.mediaType, 10);
+ if (bitnessMapper && bitnessMapper->mapProfile(pl.profile, &sdkProfile)) {
+ caps->addProfileLevel((uint32_t)sdkProfile, (uint32_t)sdkLevel);
+ }
+ }
} else if (!mapper) {
caps->addProfileLevel(pl.profile, pl.level);
}
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.cpp b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
index 4d939fa..ca6a328 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.cpp
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.cpp
@@ -381,15 +381,17 @@
{ C2Config::LEVEL_AV1_7_3, AV1Level73 },
};
-
ALookup<C2Config::profile_t, int32_t> sAv1Profiles = {
- // TODO: will need to disambiguate between Main8 and Main10
{ C2Config::PROFILE_AV1_0, AV1ProfileMain8 },
{ C2Config::PROFILE_AV1_0, AV1ProfileMain10 },
{ C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10 },
{ C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10Plus },
};
+ALookup<C2Config::profile_t, int32_t> sAv1TenbitProfiles = {
+ { C2Config::PROFILE_AV1_0, AV1ProfileMain10 },
+};
+
ALookup<C2Config::profile_t, int32_t> sAv1HdrProfiles = {
{ C2Config::PROFILE_AV1_0, AV1ProfileMain10HDR10 },
};
@@ -603,9 +605,9 @@
};
struct Av1ProfileLevelMapper : ProfileLevelMapperHelper {
- Av1ProfileLevelMapper(bool isHdr = false, bool isHdr10Plus = false) :
+ Av1ProfileLevelMapper(bool isHdr = false, bool isHdr10Plus = false, int32_t bitDepth = 8) :
ProfileLevelMapperHelper(),
- mIsHdr(isHdr), mIsHdr10Plus(isHdr10Plus) {}
+ mIsHdr(isHdr), mIsHdr10Plus(isHdr10Plus), mBitDepth(bitDepth) {}
virtual bool simpleMap(C2Config::level_t from, int32_t *to) {
return sAv1Levels.map(from, to);
@@ -614,19 +616,22 @@
return sAv1Levels.map(from, to);
}
virtual bool simpleMap(C2Config::profile_t from, int32_t *to) {
- return mIsHdr10Plus ? sAv1Hdr10PlusProfiles.map(from, to) :
- mIsHdr ? sAv1HdrProfiles.map(from, to) :
- sAv1Profiles.map(from, to);
+ return (mBitDepth == 10) ? sAv1TenbitProfiles.map(from, to) :
+ mIsHdr10Plus ? sAv1Hdr10PlusProfiles.map(from, to) :
+ mIsHdr ? sAv1HdrProfiles.map(from, to) :
+ sAv1Profiles.map(from, to);
}
virtual bool simpleMap(int32_t from, C2Config::profile_t *to) {
- return mIsHdr10Plus ? sAv1Hdr10PlusProfiles.map(from, to) :
- mIsHdr ? sAv1HdrProfiles.map(from, to) :
- sAv1Profiles.map(from, to);
+ return (mBitDepth == 10) ? sAv1TenbitProfiles.map(from, to) :
+ mIsHdr10Plus ? sAv1Hdr10PlusProfiles.map(from, to) :
+ mIsHdr ? sAv1HdrProfiles.map(from, to) :
+ sAv1Profiles.map(from, to);
}
private:
bool mIsHdr;
bool mIsHdr10Plus;
+ int32_t mBitDepth;
};
} // namespace
@@ -674,6 +679,18 @@
}
// static
+std::shared_ptr<C2Mapper::ProfileLevelMapper>
+C2Mapper::GetBitDepthProfileLevelMapper(std::string mediaType, int32_t bitDepth) {
+ std::transform(mediaType.begin(), mediaType.end(), mediaType.begin(), ::tolower);
+ if (bitDepth == 8) {
+ return GetProfileLevelMapper(mediaType);
+ } else if (mediaType == MIMETYPE_VIDEO_AV1 && bitDepth == 10) {
+ return std::make_shared<Av1ProfileLevelMapper>(false, false, bitDepth);
+ }
+ return nullptr;
+}
+
+// static
bool C2Mapper::map(C2Config::bitrate_mode_t from, int32_t *to) {
return sBitrateModes.map(from, to);
}
diff --git a/media/codec2/sfplugin/utils/Codec2Mapper.h b/media/codec2/sfplugin/utils/Codec2Mapper.h
index 797c8a8..33d305e 100644
--- a/media/codec2/sfplugin/utils/Codec2Mapper.h
+++ b/media/codec2/sfplugin/utils/Codec2Mapper.h
@@ -43,6 +43,9 @@
static std::shared_ptr<ProfileLevelMapper>
GetHdrProfileLevelMapper(std::string mediaType, bool isHdr10Plus = false);
+ static std::shared_ptr<ProfileLevelMapper>
+ GetBitDepthProfileLevelMapper(std::string mediaType, int32_t bitDepth = 8);
+
// convert between bitrates
static bool map(C2Config::bitrate_mode_t, int32_t*);
static bool map(int32_t, C2Config::bitrate_mode_t*);
diff --git a/media/libaudioclient/AidlConversion.cpp b/media/libaudioclient/AidlConversion.cpp
index 8314320..4c83406 100644
--- a/media/libaudioclient/AidlConversion.cpp
+++ b/media/libaudioclient/AidlConversion.cpp
@@ -3033,6 +3033,10 @@
return AUDIO_MODE_IN_COMMUNICATION;
case AudioMode::CALL_SCREEN:
return AUDIO_MODE_CALL_SCREEN;
+ case AudioMode::SYS_RESERVED_CALL_REDIRECT:
+ return AUDIO_MODE_CALL_REDIRECT;
+ case AudioMode::SYS_RESERVED_COMMUNICATION_REDIRECT:
+ return AUDIO_MODE_COMMUNICATION_REDIRECT;
}
return unexpected(BAD_VALUE);
}
@@ -3054,6 +3058,10 @@
return AudioMode::IN_COMMUNICATION;
case AUDIO_MODE_CALL_SCREEN:
return AudioMode::CALL_SCREEN;
+ case AUDIO_MODE_CALL_REDIRECT:
+ return AudioMode::SYS_RESERVED_CALL_REDIRECT;
+ case AUDIO_MODE_COMMUNICATION_REDIRECT:
+ return AudioMode::SYS_RESERVED_COMMUNICATION_REDIRECT;
case AUDIO_MODE_CNT:
break;
}
diff --git a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
index 80fe1ba..7f18e9e 100644
--- a/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
+++ b/media/libaudioclient/fuzzer/audioflinger_fuzzer.cpp
@@ -58,7 +58,8 @@
constexpr audio_mode_t kModes[] = {
AUDIO_MODE_INVALID, AUDIO_MODE_CURRENT, AUDIO_MODE_NORMAL, AUDIO_MODE_RINGTONE,
- AUDIO_MODE_IN_CALL, AUDIO_MODE_IN_COMMUNICATION, AUDIO_MODE_CALL_SCREEN};
+ AUDIO_MODE_IN_CALL, AUDIO_MODE_IN_COMMUNICATION, AUDIO_MODE_CALL_SCREEN,
+ AUDIO_MODE_CALL_REDIRECT, AUDIO_MODE_COMMUNICATION_REDIRECT};
constexpr audio_session_t kSessionId[] = {AUDIO_SESSION_NONE, AUDIO_SESSION_OUTPUT_STAGE,
AUDIO_SESSION_DEVICE};
diff --git a/media/libmediahelper/TypeConverter.cpp b/media/libmediahelper/TypeConverter.cpp
index 97b5b95..e29364c 100644
--- a/media/libmediahelper/TypeConverter.cpp
+++ b/media/libmediahelper/TypeConverter.cpp
@@ -30,6 +30,8 @@
MAKE_STRING_FROM_ENUM(AUDIO_MODE_IN_CALL),
MAKE_STRING_FROM_ENUM(AUDIO_MODE_IN_COMMUNICATION),
MAKE_STRING_FROM_ENUM(AUDIO_MODE_CALL_SCREEN),
+ MAKE_STRING_FROM_ENUM(AUDIO_MODE_CALL_REDIRECT),
+ MAKE_STRING_FROM_ENUM(AUDIO_MODE_COMMUNICATION_REDIRECT),
TERMINATOR
};
diff --git a/media/libmediametrics/include/media/MediaMetricsItem.h b/media/libmediametrics/include/media/MediaMetricsItem.h
index 428992c..d69f78e 100644
--- a/media/libmediametrics/include/media/MediaMetricsItem.h
+++ b/media/libmediametrics/include/media/MediaMetricsItem.h
@@ -469,16 +469,15 @@
template <> // static
status_t extract(std::string *val, const char **bufferpptr, const char *bufferptrmax) {
const char *ptr = *bufferpptr;
- while (*ptr != 0) {
+ do {
if (ptr >= bufferptrmax) {
ALOGE("%s: buffer exceeded", __func__);
return BAD_VALUE;
}
- ++ptr;
- }
- const size_t size = (ptr - *bufferpptr) + 1;
+ } while (*ptr++ != 0);
+ // ptr is terminator+1, == bufferptrmax if we finished entire buffer
*val = *bufferpptr;
- *bufferpptr += size;
+ *bufferpptr = ptr;
return NO_ERROR;
}
template <> // static
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
index 00c38d5..1ea3f99 100644
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -1511,6 +1511,9 @@
uint32_t flags) {
sp<AMessage> msg = new AMessage(kWhatConfigure, this);
+ // TODO: validity check log-session-id: it should be a 32-hex-digit.
+ format->findString("log-session-id", &mLogSessionId);
+
if (mMetricsHandle != 0) {
int32_t profile = 0;
if (format->findInt32("profile", &profile)) {
@@ -1522,11 +1525,11 @@
}
mediametrics_setInt32(mMetricsHandle, kCodecEncoder,
(flags & CONFIGURE_FLAG_ENCODE) ? 1 : 0);
+
+ mediametrics_setCString(mMetricsHandle, kCodecLogSessionId, mLogSessionId.c_str());
}
if (mIsVideo) {
- // TODO: validity check log-session-id: it should be a 32-hex-digit.
- format->findString("log-session-id", &mLogSessionId);
format->findInt32("width", &mVideoWidth);
format->findInt32("height", &mVideoHeight);
if (!format->findInt32("rotation-degrees", &mRotationDegrees)) {
@@ -1534,7 +1537,6 @@
}
if (mMetricsHandle != 0) {
- mediametrics_setCString(mMetricsHandle, kCodecLogSessionId, mLogSessionId.c_str());
mediametrics_setInt32(mMetricsHandle, kCodecWidth, mVideoWidth);
mediametrics_setInt32(mMetricsHandle, kCodecHeight, mVideoHeight);
mediametrics_setInt32(mMetricsHandle, kCodecRotation, mRotationDegrees);
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 4c18f87..a6df5bb 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -1759,24 +1759,39 @@
if (mime.startsWith("video/") || mime.startsWith("image/")) {
int32_t width;
int32_t height;
- if (msg->findInt32("width", &width) && msg->findInt32("height", &height)) {
- meta->setInt32(kKeyWidth, width);
- meta->setInt32(kKeyHeight, height);
- } else {
+ if (!msg->findInt32("width", &width) || !msg->findInt32("height", &height)) {
ALOGV("did not find width and/or height");
return BAD_VALUE;
}
+ if (width <= 0 || height <= 0) {
+ ALOGE("Invalid value of width: %d and/or height: %d", width, height);
+ return BAD_VALUE;
+ }
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
- int32_t sarWidth, sarHeight;
- if (msg->findInt32("sar-width", &sarWidth)
- && msg->findInt32("sar-height", &sarHeight)) {
+ int32_t sarWidth = -1, sarHeight = -1;
+ bool foundWidth, foundHeight;
+ foundWidth = msg->findInt32("sar-width", &sarWidth);
+ foundHeight = msg->findInt32("sar-height", &sarHeight);
+ if (foundWidth || foundHeight) {
+ if (sarWidth <= 0 || sarHeight <= 0) {
+ ALOGE("Invalid value of sarWidth: %d and/or sarHeight: %d", sarWidth, sarHeight);
+ return BAD_VALUE;
+ }
meta->setInt32(kKeySARWidth, sarWidth);
meta->setInt32(kKeySARHeight, sarHeight);
}
- int32_t displayWidth, displayHeight;
- if (msg->findInt32("display-width", &displayWidth)
- && msg->findInt32("display-height", &displayHeight)) {
+ int32_t displayWidth = -1, displayHeight = -1;
+ foundWidth = msg->findInt32("display-width", &displayWidth);
+ foundHeight = msg->findInt32("display-height", &displayHeight);
+ if (foundWidth || foundHeight) {
+ if (displayWidth <= 0 || displayHeight <= 0) {
+ ALOGE("Invalid value of displayWidth: %d and/or displayHeight: %d",
+ displayWidth, displayHeight);
+ return BAD_VALUE;
+ }
meta->setInt32(kKeyDisplayWidth, displayWidth);
meta->setInt32(kKeyDisplayHeight, displayHeight);
}
@@ -1786,17 +1801,29 @@
if (msg->findInt32("is-default", &isPrimary) && isPrimary) {
meta->setInt32(kKeyTrackIsDefault, 1);
}
- int32_t tileWidth, tileHeight, gridRows, gridCols;
- if (msg->findInt32("tile-width", &tileWidth)) {
+ int32_t tileWidth = -1, tileHeight = -1;
+ foundWidth = msg->findInt32("tile-width", &tileWidth);
+ foundHeight = msg->findInt32("tile-height", &tileHeight);
+ if (foundWidth || foundHeight) {
+ if (tileWidth <= 0 || tileHeight <= 0) {
+ ALOGE("Invalid value of tileWidth: %d and/or tileHeight: %d",
+ tileWidth, tileHeight);
+ return BAD_VALUE;
+ }
meta->setInt32(kKeyTileWidth, tileWidth);
- }
- if (msg->findInt32("tile-height", &tileHeight)) {
meta->setInt32(kKeyTileHeight, tileHeight);
}
- if (msg->findInt32("grid-rows", &gridRows)) {
+ int32_t gridRows = -1, gridCols = -1;
+ bool foundRows, foundCols;
+ foundRows = msg->findInt32("grid-rows", &gridRows);
+ foundCols = msg->findInt32("grid-cols", &gridCols);
+ if (foundRows || foundCols) {
+ if (gridRows <= 0 || gridCols <= 0) {
+ ALOGE("Invalid value of gridRows: %d and/or gridCols: %d",
+ gridRows, gridCols);
+ return BAD_VALUE;
+ }
meta->setInt32(kKeyGridRows, gridRows);
- }
- if (msg->findInt32("grid-cols", &gridCols)) {
meta->setInt32(kKeyGridCols, gridCols);
}
}
@@ -1812,6 +1839,14 @@
&cropTop,
&cropRight,
&cropBottom)) {
+ if (cropLeft < 0 || cropLeft > cropRight || cropRight >= width) {
+ ALOGE("Invalid value of cropLeft: %d and/or cropRight: %d", cropLeft, cropRight);
+ return BAD_VALUE;
+ }
+ if (cropTop < 0 || cropTop > cropBottom || cropBottom >= height) {
+ ALOGE("Invalid value of cropTop: %d and/or cropBottom: %d", cropTop, cropBottom);
+ return BAD_VALUE;
+ }
meta->setRect(kKeyCropRect, cropLeft, cropTop, cropRight, cropBottom);
}
@@ -1855,9 +1890,16 @@
ALOGV("did not find channel-count and/or sample-rate");
return BAD_VALUE;
}
+ // channel count can be zero in some cases like mpeg h
+ if (sampleRate <= 0 || numChannels < 0) {
+ ALOGE("Invalid value of channel-count: %d and/or sample-rate: %d",
+ numChannels, sampleRate);
+ return BAD_VALUE;
+ }
meta->setInt32(kKeyChannelCount, numChannels);
meta->setInt32(kKeySampleRate, sampleRate);
int32_t bitsPerSample;
+ // TODO:(b/204430952) add appropriate bound check for bitsPerSample
if (msg->findInt32("bits-per-sample", &bitsPerSample)) {
meta->setInt32(kKeyBitsPerSample, bitsPerSample);
}
diff --git a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
index 4237e8c..3a01925 100644
--- a/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
+++ b/media/libstagefright/include/media/stagefright/MediaCodecConstants.h
@@ -364,7 +364,7 @@
inline static const char *asString_AV1Profile(int32_t i, const char *def = "??") {
switch (i) {
case AV1ProfileMain8: return "Main8";
- case AV1ProfileMain10: return "Main10HDR";
+ case AV1ProfileMain10: return "Main10";
case AV1ProfileMain10HDR10: return "Main10HDR10";
case AV1ProfileMain10HDR10Plus: return "Main10HDRPlus";
default: return def;
diff --git a/media/utils/Android.bp b/media/utils/Android.bp
index 88b822d..c1c7df5 100644
--- a/media/utils/Android.bp
+++ b/media/utils/Android.bp
@@ -34,6 +34,7 @@
"SchedulingPolicyService.cpp",
"ServiceUtilities.cpp",
"TimeCheck.cpp",
+ "TimerThread.cpp",
],
static_libs: [
"libc_malloc_debug_backtrace",
@@ -118,3 +119,12 @@
export_include_dirs: ["include"],
}
+
+cc_test {
+ name: "libmediautils_test",
+ srcs: ["TimerThread-test.cpp"],
+ shared_libs: [
+ "libmediautils",
+ "libutils",
+ ]
+}
diff --git a/media/utils/TimeCheck.cpp b/media/utils/TimeCheck.cpp
index 819e146..878ae8c 100644
--- a/media/utils/TimeCheck.cpp
+++ b/media/utils/TimeCheck.cpp
@@ -16,9 +16,11 @@
#define LOG_TAG "TimeCheck"
-#include <utils/Log.h>
-#include <mediautils/TimeCheck.h>
+#include <optional>
+
#include <mediautils/EventLog.h>
+#include <mediautils/TimeCheck.h>
+#include <utils/Log.h>
#include "debuggerd/handler.h"
namespace android {
@@ -58,84 +60,35 @@
}
/* static */
-sp<TimeCheck::TimeCheckThread> TimeCheck::getTimeCheckThread()
-{
- static sp<TimeCheck::TimeCheckThread> sTimeCheckThread = new TimeCheck::TimeCheckThread();
+TimerThread* TimeCheck::getTimeCheckThread() {
+ static TimerThread* sTimeCheckThread = new TimerThread();
return sTimeCheckThread;
}
-TimeCheck::TimeCheck(const char *tag, uint32_t timeoutMs)
- : mEndTimeNs(getTimeCheckThread()->startMonitoring(tag, timeoutMs))
-{
-}
+TimeCheck::TimeCheck(const char* tag, uint32_t timeoutMs)
+ : mTimerHandle(getTimeCheckThread()->scheduleTask([tag] { crash(tag); },
+ std::chrono::milliseconds(timeoutMs))) {}
TimeCheck::~TimeCheck() {
- getTimeCheckThread()->stopMonitoring(mEndTimeNs);
+ getTimeCheckThread()->cancelTask(mTimerHandle);
}
-TimeCheck::TimeCheckThread::~TimeCheckThread()
-{
- AutoMutex _l(mMutex);
- requestExit();
- mMonitorRequests.clear();
- mCond.signal();
-}
-
-nsecs_t TimeCheck::TimeCheckThread::startMonitoring(const char *tag, uint32_t timeoutMs) {
- Mutex::Autolock _l(mMutex);
- nsecs_t endTimeNs = systemTime() + milliseconds(timeoutMs);
- for (; mMonitorRequests.indexOfKey(endTimeNs) >= 0; ++endTimeNs);
- mMonitorRequests.add(endTimeNs, tag);
- mCond.signal();
- return endTimeNs;
-}
-
-void TimeCheck::TimeCheckThread::stopMonitoring(nsecs_t endTimeNs) {
- Mutex::Autolock _l(mMutex);
- mMonitorRequests.removeItem(endTimeNs);
- mCond.signal();
-}
-
-bool TimeCheck::TimeCheckThread::threadLoop()
-{
- status_t status = TIMED_OUT;
- {
- AutoMutex _l(mMutex);
-
- if (exitPending()) {
- return false;
+/* static */
+void TimeCheck::crash(const char* tag) {
+ // Generate audio HAL processes tombstones and allow time to complete
+ // before forcing restart
+ std::vector<pid_t> pids = getAudioHalPids();
+ if (pids.size() != 0) {
+ for (const auto& pid : pids) {
+ ALOGI("requesting tombstone for pid: %d", pid);
+ sigqueue(pid, DEBUGGER_SIGNAL, {.sival_int = 0});
}
-
- nsecs_t endTimeNs = INT64_MAX;
- const char *tag = "<unspecified>";
- // KeyedVector mMonitorRequests is ordered so take first entry as next timeout
- if (mMonitorRequests.size() != 0) {
- endTimeNs = mMonitorRequests.keyAt(0);
- tag = mMonitorRequests.valueAt(0);
- }
-
- const nsecs_t waitTimeNs = endTimeNs - systemTime();
- if (waitTimeNs > 0) {
- status = mCond.waitRelative(mMutex, waitTimeNs);
- }
- if (status != NO_ERROR) {
- // Generate audio HAL processes tombstones and allow time to complete
- // before forcing restart
- std::vector<pid_t> pids = getAudioHalPids();
- if (pids.size() != 0) {
- for (const auto& pid : pids) {
- ALOGI("requesting tombstone for pid: %d", pid);
- sigqueue(pid, DEBUGGER_SIGNAL, {.sival_int = 0});
- }
- sleep(1);
- } else {
- ALOGI("No HAL process pid available, skipping tombstones");
- }
- LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag);
- LOG_ALWAYS_FATAL("TimeCheck timeout for %s", tag);
- }
+ sleep(1);
+ } else {
+ ALOGI("No HAL process pid available, skipping tombstones");
}
- return true;
+ LOG_EVENT_STRING(LOGTAG_AUDIO_BINDER_TIMEOUT, tag);
+ LOG_ALWAYS_FATAL("TimeCheck timeout for %s", tag);
}
-}; // namespace android
+}; // namespace android
diff --git a/media/utils/TimerThread-test.cpp b/media/utils/TimerThread-test.cpp
new file mode 100644
index 0000000..ee8a811
--- /dev/null
+++ b/media/utils/TimerThread-test.cpp
@@ -0,0 +1,146 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <chrono>
+#include <thread>
+#include <gtest/gtest.h>
+#include <mediautils/TimerThread.h>
+
+using namespace std::chrono_literals;
+
+namespace android {
+namespace {
+
+constexpr auto kJitter = 10ms;
+
+TEST(TimerThread, Basic) {
+ std::atomic<bool> taskRan = false;
+ TimerThread thread;
+ thread.scheduleTask([&taskRan] { taskRan = true; }, 100ms);
+ std::this_thread::sleep_for(100ms - kJitter);
+ ASSERT_FALSE(taskRan);
+ std::this_thread::sleep_for(2 * kJitter);
+ ASSERT_TRUE(taskRan);
+}
+
+TEST(TimerThread, Cancel) {
+ std::atomic<bool> taskRan = false;
+ TimerThread thread;
+ TimerThread::Handle handle = thread.scheduleTask([&taskRan] { taskRan = true; }, 100ms);
+ std::this_thread::sleep_for(100ms - kJitter);
+ ASSERT_FALSE(taskRan);
+ thread.cancelTask(handle);
+ std::this_thread::sleep_for(2 * kJitter);
+ ASSERT_FALSE(taskRan);
+}
+
+TEST(TimerThread, CancelAfterRun) {
+ std::atomic<bool> taskRan = false;
+ TimerThread thread;
+ TimerThread::Handle handle = thread.scheduleTask([&taskRan] { taskRan = true; }, 100ms);
+ std::this_thread::sleep_for(100ms + kJitter);
+ ASSERT_TRUE(taskRan);
+ thread.cancelTask(handle);
+}
+
+TEST(TimerThread, MultipleTasks) {
+ std::array<std::atomic<bool>, 6> taskRan;
+ TimerThread thread;
+
+ auto startTime = std::chrono::steady_clock::now();
+
+ thread.scheduleTask([&taskRan] { taskRan[0] = true; }, 300ms);
+ thread.scheduleTask([&taskRan] { taskRan[1] = true; }, 100ms);
+ thread.scheduleTask([&taskRan] { taskRan[2] = true; }, 200ms);
+ thread.scheduleTask([&taskRan] { taskRan[3] = true; }, 400ms);
+ auto handle4 = thread.scheduleTask([&taskRan] { taskRan[4] = true; }, 200ms);
+ thread.scheduleTask([&taskRan] { taskRan[5] = true; }, 200ms);
+
+ // Task 1 should trigger around 100ms.
+ std::this_thread::sleep_until(startTime + 100ms - kJitter);
+ ASSERT_FALSE(taskRan[0]);
+ ASSERT_FALSE(taskRan[1]);
+ ASSERT_FALSE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_FALSE(taskRan[5]);
+
+ std::this_thread::sleep_until(startTime + 100ms + kJitter);
+ ASSERT_FALSE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_FALSE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_FALSE(taskRan[5]);
+
+ // Cancel task 4 before it gets a chance to run.
+ thread.cancelTask(handle4);
+
+ // Tasks 2 and 5 should trigger around 200ms.
+ std::this_thread::sleep_until(startTime + 200ms - kJitter);
+ ASSERT_FALSE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_FALSE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_FALSE(taskRan[5]);
+
+ std::this_thread::sleep_until(startTime + 200ms + kJitter);
+ ASSERT_FALSE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_TRUE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_TRUE(taskRan[5]);
+
+ // Task 0 should trigger around 300ms.
+ std::this_thread::sleep_until(startTime + 300ms - kJitter);
+ ASSERT_FALSE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_TRUE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_TRUE(taskRan[5]);
+
+ std::this_thread::sleep_until(startTime + 300ms + kJitter);
+ ASSERT_TRUE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_TRUE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_TRUE(taskRan[5]);
+
+ // Task 3 should trigger around 400ms.
+ std::this_thread::sleep_until(startTime + 400ms - kJitter);
+ ASSERT_TRUE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_TRUE(taskRan[2]);
+ ASSERT_FALSE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_TRUE(taskRan[5]);
+
+ std::this_thread::sleep_until(startTime + 400ms + kJitter);
+ ASSERT_TRUE(taskRan[0]);
+ ASSERT_TRUE(taskRan[1]);
+ ASSERT_TRUE(taskRan[2]);
+ ASSERT_TRUE(taskRan[3]);
+ ASSERT_FALSE(taskRan[4]);
+ ASSERT_TRUE(taskRan[5]);
+}
+
+
+} // namespace
+} // namespace android
diff --git a/media/utils/TimerThread.cpp b/media/utils/TimerThread.cpp
new file mode 100644
index 0000000..3c95798
--- /dev/null
+++ b/media/utils/TimerThread.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2021 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_TAG "TimerThread"
+
+#include <optional>
+
+#include <mediautils/TimerThread.h>
+#include <utils/ThreadDefs.h>
+
+namespace android {
+
+TimerThread::TimerThread() : mThread([this] { threadFunc(); }) {
+ pthread_setname_np(mThread.native_handle(), "TimeCheckThread");
+ pthread_setschedprio(mThread.native_handle(), PRIORITY_URGENT_AUDIO);
+}
+
+TimerThread::~TimerThread() {
+ {
+ std::lock_guard _l(mMutex);
+ mShouldExit = true;
+ mCond.notify_all();
+ }
+ mThread.join();
+}
+
+TimerThread::Handle TimerThread::scheduleTaskAtDeadline(std::function<void()>&& func,
+ TimePoint deadline) {
+ std::lock_guard _l(mMutex);
+
+ // To avoid key collisions, advance by 1 tick until the key is unique.
+ for (; mMonitorRequests.find(deadline) != mMonitorRequests.end();
+ deadline += TimePoint::duration(1))
+ ;
+ mMonitorRequests.emplace(deadline, std::move(func));
+ mCond.notify_all();
+ return deadline;
+}
+
+void TimerThread::cancelTask(Handle handle) {
+ std::lock_guard _l(mMutex);
+ mMonitorRequests.erase(handle);
+}
+
+void TimerThread::threadFunc() {
+ std::unique_lock _l(mMutex);
+
+ while (!mShouldExit) {
+ if (!mMonitorRequests.empty()) {
+ TimePoint nextDeadline = mMonitorRequests.begin()->first;
+ if (nextDeadline < std::chrono::steady_clock::now()) {
+ // Deadline expired.
+ mMonitorRequests.begin()->second();
+ mMonitorRequests.erase(mMonitorRequests.begin());
+ }
+ mCond.wait_until(_l, nextDeadline);
+ } else {
+ mCond.wait(_l);
+ }
+ }
+}
+
+} // namespace android
diff --git a/media/utils/include/mediautils/TimeCheck.h b/media/utils/include/mediautils/TimeCheck.h
index 5ba6d7c..2411f97 100644
--- a/media/utils/include/mediautils/TimeCheck.h
+++ b/media/utils/include/mediautils/TimeCheck.h
@@ -14,62 +14,33 @@
* limitations under the License.
*/
+#pragma once
-#ifndef ANDROID_TIME_CHECK_H
-#define ANDROID_TIME_CHECK_H
-
-#include <utils/KeyedVector.h>
-#include <utils/Thread.h>
#include <vector>
+#include <mediautils/TimerThread.h>
+
namespace android {
// A class monitoring execution time for a code block (scoped variable) and causing an assert
// if it exceeds a certain time
class TimeCheck {
-public:
-
+ public:
// The default timeout is chosen to be less than system server watchdog timeout
static constexpr uint32_t kDefaultTimeOutMs = 5000;
- TimeCheck(const char *tag, uint32_t timeoutMs = kDefaultTimeOutMs);
- ~TimeCheck();
- static void setAudioHalPids(const std::vector<pid_t>& pids);
- static std::vector<pid_t> getAudioHalPids();
+ TimeCheck(const char* tag, uint32_t timeoutMs = kDefaultTimeOutMs);
+ ~TimeCheck();
+ static void setAudioHalPids(const std::vector<pid_t>& pids);
+ static std::vector<pid_t> getAudioHalPids();
-private:
-
- class TimeCheckThread : public Thread {
- public:
-
- TimeCheckThread() {}
- virtual ~TimeCheckThread() override;
-
- nsecs_t startMonitoring(const char *tag, uint32_t timeoutMs);
- void stopMonitoring(nsecs_t endTimeNs);
-
- private:
-
- // RefBase
- virtual void onFirstRef() override { run("TimeCheckThread", PRIORITY_URGENT_AUDIO); }
-
- // Thread
- virtual bool threadLoop() override;
-
- Condition mCond;
- Mutex mMutex;
- // using the end time in ns as key is OK given the risk is low that two entries
- // are added in such a way that <add time> + <timeout> are the same for both.
- KeyedVector< nsecs_t, const char*> mMonitorRequests;
- };
-
- static sp<TimeCheckThread> getTimeCheckThread();
+ private:
+ static TimerThread* getTimeCheckThread();
static void accessAudioHalPids(std::vector<pid_t>* pids, bool update);
+ static void crash(const char* tag);
- const nsecs_t mEndTimeNs;
+ const TimerThread::Handle mTimerHandle;
};
-}; // namespace android
-
-#endif // ANDROID_TIME_CHECK_H
+}; // namespace android
diff --git a/media/utils/include/mediautils/TimerThread.h b/media/utils/include/mediautils/TimerThread.h
new file mode 100644
index 0000000..cf457b8
--- /dev/null
+++ b/media/utils/include/mediautils/TimerThread.h
@@ -0,0 +1,68 @@
+/*
+ * Copyright (C) 2021 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.
+ */
+
+#pragma once
+
+#include <condition_variable>
+#include <functional>
+#include <map>
+#include <mutex>
+#include <thread>
+
+#include <android-base/thread_annotations.h>
+
+namespace android {
+
+/**
+ * A thread for deferred execution of tasks, with cancellation.
+ */
+class TimerThread {
+ public:
+ using Handle = std::chrono::steady_clock::time_point;
+
+ TimerThread();
+ ~TimerThread();
+
+ /**
+ * Schedule a task to be executed in the future (`timeout` duration from now).
+ * Returns a handle that can be used for cancellation.
+ */
+ template <typename R, typename P>
+ Handle scheduleTask(std::function<void()>&& func, std::chrono::duration<R, P> timeout) {
+ auto deadline = std::chrono::steady_clock::now() + std::chrono::milliseconds(timeout);
+ return scheduleTaskAtDeadline(std::move(func), deadline);
+ }
+
+ /**
+ * Cancel a task, previously scheduled with scheduleTask().
+ * If the task has already executed, this is a no-op.
+ */
+ void cancelTask(Handle handle);
+
+ private:
+ using TimePoint = std::chrono::steady_clock::time_point;
+
+ std::condition_variable mCond;
+ std::mutex mMutex;
+ std::thread mThread;
+ std::map<TimePoint, std::function<void()>> mMonitorRequests GUARDED_BY(mMutex);
+ bool mShouldExit GUARDED_BY(mMutex) = false;
+
+ void threadFunc();
+ Handle scheduleTaskAtDeadline(std::function<void()>&& func, TimePoint deadline);
+};
+
+} // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index daed5fc..5ebf483 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -565,6 +565,12 @@
: mChain(owner)
, mThread(thread)
, mAudioFlinger(*gAudioFlinger) {
+ sp<ThreadBase> base = thread.promote();
+ if (base != nullptr) {
+ mThreadType = base->type();
+ } else {
+ mThreadType = ThreadBase::MIXER; // assure a consistent value.
+ }
}
status_t createEffectHal(const effect_uuid_t *pEffectUuid,
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index c8d286c..92140d5 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -6133,11 +6133,11 @@
uid_t uid;
sp<RecordClientDescriptor> topClient = inputDesc->getHighestPriorityClient();
if (topClient != nullptr) {
- attributes = topClient->attributes();
- uid = topClient->uid();
+ attributes = topClient->attributes();
+ uid = topClient->uid();
} else {
- attributes = { .source = AUDIO_SOURCE_DEFAULT };
- uid = 0;
+ attributes = { .source = AUDIO_SOURCE_DEFAULT };
+ uid = 0;
}
if (attributes.source == AUDIO_SOURCE_DEFAULT && isInCall()) {
@@ -6937,8 +6937,8 @@
{
audio_mode_t mode = mEngine->getPhoneState();
return (mode == AUDIO_MODE_IN_CALL)
- || (mode == AUDIO_MODE_IN_COMMUNICATION)
- || (mode == AUDIO_MODE_CALL_SCREEN);
+ || (mode == AUDIO_MODE_CALL_SCREEN)
+ || (mode == AUDIO_MODE_CALL_REDIRECT);
}
void AudioPolicyManager::cleanUpForDevice(const sp<DeviceDescriptor>& deviceDesc)
diff --git a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
index 29baa22..099a49e 100644
--- a/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
+++ b/services/audiopolicy/service/AudioPolicyInterfaceImpl.cpp
@@ -219,7 +219,15 @@
// can be interleaved).
Mutex::Autolock _l(mLock);
// TODO: check if it is more appropriate to do it in platform specific policy manager
- AudioSystem::setMode(state);
+
+ // Audio HAL mode conversion for call redirect modes
+ audio_mode_t halMode = state;
+ if (state == AUDIO_MODE_CALL_REDIRECT) {
+ halMode = AUDIO_MODE_CALL_SCREEN;
+ } else if (state == AUDIO_MODE_COMMUNICATION_REDIRECT) {
+ halMode = AUDIO_MODE_NORMAL;
+ }
+ AudioSystem::setMode(halMode);
AutoCallerClear acc;
mAudioPolicyManager->setPhoneState(state);
diff --git a/services/camera/libcameraservice/Android.bp b/services/camera/libcameraservice/Android.bp
index 26562e0..a24b35d 100644
--- a/services/camera/libcameraservice/Android.bp
+++ b/services/camera/libcameraservice/Android.bp
@@ -84,6 +84,7 @@
"device3/Camera3OutputUtils.cpp",
"device3/Camera3DeviceInjectionMethods.cpp",
"device3/UHRCropAndMeteringRegionMapper.cpp",
+ "device3/PreviewFrameScheduler.cpp",
"gui/RingBufferConsumer.cpp",
"hidl/AidlCameraDeviceCallbacks.cpp",
"hidl/AidlCameraServiceListener.cpp",
@@ -107,6 +108,7 @@
],
shared_libs: [
+ "libandroid",
"libbase",
"libdl",
"libexif",
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
index 3738d01..d8bcc8a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.cpp
@@ -22,6 +22,7 @@
#include <fstream>
#include <android-base/unique_fd.h>
+#include <cutils/properties.h>
#include <ui/GraphicBuffer.h>
#include <utils/Log.h>
#include <utils/Trace.h>
@@ -347,20 +348,6 @@
mTraceFirstBuffer = false;
}
- if (transform != -1) {
- setTransformLocked(transform);
- }
-
- /* Certain consumers (such as AudioSource or HardwareComposer) use
- * MONOTONIC time, causing time misalignment if camera timestamp is
- * in BOOTTIME. Do the conversion if necessary. */
- res = native_window_set_buffers_timestamp(mConsumer.get(),
- mUseMonoTimestamp ? timestamp - mTimestampOffset : timestamp);
- if (res != OK) {
- ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
- __FUNCTION__, mId, strerror(-res), res);
- return res;
- }
// If this is a JPEG output, and image dump mask is set, save image to
// disk.
if (getFormat() == HAL_PIXEL_FORMAT_BLOB && getDataSpace() == HAL_DATASPACE_V0_JFIF &&
@@ -368,10 +355,31 @@
dumpImageToDisk(timestamp, anwBuffer, anwReleaseFence);
}
- res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
- if (shouldLogError(res, state)) {
- ALOGE("%s: Stream %d: Error queueing buffer to native window:"
- " %s (%d)", __FUNCTION__, mId, strerror(-res), res);
+ /* Certain consumers (such as AudioSource or HardwareComposer) use
+ * MONOTONIC time, causing time misalignment if camera timestamp is
+ * in BOOTTIME. Do the conversion if necessary. */
+ nsecs_t adjustedTs = mUseMonoTimestamp ? timestamp - mTimestampOffset : timestamp;
+ if (mPreviewFrameScheduler != nullptr) {
+ res = mPreviewFrameScheduler->queuePreviewBuffer(adjustedTs, transform,
+ anwBuffer, anwReleaseFence);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Error queuing buffer to preview buffer scheduler: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ } else {
+ setTransform(transform);
+ res = native_window_set_buffers_timestamp(mConsumer.get(), adjustedTs);
+ if (res != OK) {
+ ALOGE("%s: Stream %d: Error setting timestamp: %s (%d)",
+ __FUNCTION__, mId, strerror(-res), res);
+ return res;
+ }
+ res = queueBufferToConsumer(currentConsumer, anwBuffer, anwReleaseFence, surface_ids);
+ if (shouldLogError(res, state)) {
+ ALOGE("%s: Stream %d: Error queueing buffer to native window:"
+ " %s (%d)", __FUNCTION__, mId, strerror(-res), res);
+ }
}
}
mLock.lock();
@@ -412,6 +420,9 @@
status_t Camera3OutputStream::setTransformLocked(int transform) {
status_t res = OK;
+
+ if (transform == -1) return res;
+
if (mState == STATE_ERROR) {
ALOGE("%s: Stream in error state", __FUNCTION__);
return INVALID_OPERATION;
@@ -437,7 +448,7 @@
return res;
}
- if ((res = configureConsumerQueueLocked()) != OK) {
+ if ((res = configureConsumerQueueLocked(true /*allowPreviewScheduler*/)) != OK) {
return res;
}
@@ -461,7 +472,7 @@
return OK;
}
-status_t Camera3OutputStream::configureConsumerQueueLocked() {
+status_t Camera3OutputStream::configureConsumerQueueLocked(bool allowPreviewScheduler) {
status_t res;
mTraceFirstBuffer = true;
@@ -547,6 +558,15 @@
}
mTotalBufferCount = maxConsumerBuffers + camera_stream::max_buffers;
+ if (allowPreviewScheduler && isConsumedByHWComposer()) {
+ // We cannot distinguish between a SurfaceView and an ImageReader of
+ // preview buffer format. The PreviewFrameScheduler needs to handle both.
+ if (!property_get_bool("camera.disable_preview_scheduler", false)) {
+ mPreviewFrameScheduler = std::make_unique<PreviewFrameScheduler>(*this, mConsumer);
+ mTotalBufferCount += PreviewFrameScheduler::kQueueDepthWatermark;
+ }
+ }
+
mHandoutTotalBufferCount = 0;
mFrameCount = 0;
mLastTimestamp = 0;
@@ -1185,6 +1205,11 @@
}
}
+bool Camera3OutputStream::shouldLogError(status_t res) {
+ Mutex::Autolock l(mLock);
+ return shouldLogError(res, mState);
+}
+
}; // namespace camera3
}; // namespace android
diff --git a/services/camera/libcameraservice/device3/Camera3OutputStream.h b/services/camera/libcameraservice/device3/Camera3OutputStream.h
index 0872687..b2b574a 100644
--- a/services/camera/libcameraservice/device3/Camera3OutputStream.h
+++ b/services/camera/libcameraservice/device3/Camera3OutputStream.h
@@ -27,6 +27,7 @@
#include "Camera3IOStreamBase.h"
#include "Camera3OutputStreamInterface.h"
#include "Camera3BufferManager.h"
+#include "PreviewFrameScheduler.h"
namespace android {
@@ -229,6 +230,7 @@
static void applyZSLUsageQuirk(int format, uint64_t *consumerUsage /*inout*/);
void setImageDumpMask(int mask) { mImageDumpMask = mask; }
+ bool shouldLogError(status_t res);
protected:
Camera3OutputStream(int id, camera_stream_type_t type,
@@ -255,7 +257,7 @@
status_t getEndpointUsageForSurface(uint64_t *usage,
const sp<Surface>& surface) const;
- status_t configureConsumerQueueLocked();
+ status_t configureConsumerQueueLocked(bool allowPreviewScheduler);
// Consumer as the output of camera HAL
sp<Surface> mConsumer;
@@ -370,6 +372,8 @@
int mImageDumpMask = 0;
+ // The preview stream scheduler for re-timing frames
+ std::unique_ptr<PreviewFrameScheduler> mPreviewFrameScheduler;
}; // class Camera3OutputStream
} // namespace camera3
diff --git a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
index 15cf7f4..9e0c8f3 100644
--- a/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
+++ b/services/camera/libcameraservice/device3/Camera3SharedOutputStream.cpp
@@ -247,7 +247,7 @@
return res;
}
- res = configureConsumerQueueLocked();
+ res = configureConsumerQueueLocked(false/*allowPreviewScheduler*/);
if (res != OK) {
ALOGE("Failed to configureConsumerQueueLocked: %s(%d)", strerror(-res), res);
return res;
diff --git a/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp b/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
new file mode 100644
index 0000000..1fbdb18
--- /dev/null
+++ b/services/camera/libcameraservice/device3/PreviewFrameScheduler.cpp
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2021 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_TAG "Camera3-PreviewFrameScheduler"
+#define ATRACE_TAG ATRACE_TAG_CAMERA
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+#include <utils/Trace.h>
+
+#include <android/looper.h>
+#include "PreviewFrameScheduler.h"
+#include "Camera3OutputStream.h"
+
+namespace android {
+
+namespace camera3 {
+
+/**
+ * Internal Choreographer thread implementation for polling and handling callbacks
+ */
+
+// Callback function for Choreographer
+static void frameCallback(const AChoreographerFrameCallbackData* callbackData, void* data) {
+ PreviewFrameScheduler* parent = static_cast<PreviewFrameScheduler*>(data);
+ if (parent == nullptr) {
+ ALOGE("%s: Invalid data for Choreographer callback!", __FUNCTION__);
+ return;
+ }
+
+ size_t length = AChoreographerFrameCallbackData_getFrameTimelinesLength(callbackData);
+ std::vector<nsecs_t> timeline(length);
+ for (size_t i = 0; i < length; i++) {
+ nsecs_t timestamp = AChoreographerFrameCallbackData_getFrameTimelineExpectedPresentTime(
+ callbackData, i);
+ timeline[i] = timestamp;
+ }
+
+ parent->onNewPresentationTime(timeline);
+
+ AChoreographer_postExtendedFrameCallback(AChoreographer_getInstance(), frameCallback, data);
+}
+
+struct ChoreographerThread : public Thread {
+ ChoreographerThread();
+ status_t start(PreviewFrameScheduler* parent);
+ virtual status_t readyToRun() override;
+ virtual bool threadLoop() override;
+
+protected:
+ virtual ~ChoreographerThread() {}
+
+private:
+ ChoreographerThread &operator=(const ChoreographerThread &);
+
+ // This only impacts the shutdown time. It won't impact the choreographer
+ // callback frequency.
+ static constexpr nsecs_t kPollingTimeoutMs = 5;
+ PreviewFrameScheduler* mParent = nullptr;
+};
+
+ChoreographerThread::ChoreographerThread() : Thread(false /*canCallJava*/) {
+}
+
+status_t ChoreographerThread::start(PreviewFrameScheduler* parent) {
+ mParent = parent;
+ return run("PreviewChoreographer");
+}
+
+status_t ChoreographerThread::readyToRun() {
+ ALooper_prepare(ALOOPER_PREPARE_ALLOW_NON_CALLBACKS);
+ if (AChoreographer_getInstance() == NULL) {
+ return NO_INIT;
+ }
+
+ AChoreographer_postExtendedFrameCallback(
+ AChoreographer_getInstance(), frameCallback, mParent);
+ return OK;
+}
+
+bool ChoreographerThread::threadLoop() {
+ if (exitPending()) {
+ return false;
+ }
+ ALooper_pollOnce(kPollingTimeoutMs, nullptr, nullptr, nullptr);
+ return true;
+}
+
+/**
+ * PreviewFrameScheduler implementation
+ */
+
+PreviewFrameScheduler::PreviewFrameScheduler(Camera3OutputStream& parent, sp<Surface> consumer) :
+ mParent(parent),
+ mConsumer(consumer),
+ mChoreographerThread(new ChoreographerThread()) {
+}
+
+PreviewFrameScheduler::~PreviewFrameScheduler() {
+ {
+ Mutex::Autolock l(mLock);
+ mChoreographerThread->requestExit();
+ }
+ mChoreographerThread->join();
+}
+
+status_t PreviewFrameScheduler::queuePreviewBuffer(nsecs_t timestamp, int32_t transform,
+ ANativeWindowBuffer* anwBuffer, int releaseFence) {
+ // Start choreographer thread if it's not already running.
+ if (!mChoreographerThread->isRunning()) {
+ status_t res = mChoreographerThread->start(this);
+ if (res != OK) {
+ ALOGE("%s: Failed to init choreographer thread!", __FUNCTION__);
+ return res;
+ }
+ }
+
+ {
+ Mutex::Autolock l(mLock);
+ mPendingBuffers.emplace(timestamp, transform, anwBuffer, releaseFence);
+
+ // Queue buffer to client right away if pending buffers are more than
+ // the queue depth watermark.
+ if (mPendingBuffers.size() > kQueueDepthWatermark) {
+ auto oldBuffer = mPendingBuffers.front();
+ mPendingBuffers.pop();
+
+ status_t res = queueBufferToClientLocked(oldBuffer, oldBuffer.timestamp);
+ if (res != OK) {
+ return res;
+ }
+
+ // Reset the last capture and presentation time
+ mLastCameraCaptureTime = 0;
+ mLastCameraPresentTime = 0;
+ } else {
+ ATRACE_INT(kPendingBufferTraceName, mPendingBuffers.size());
+ }
+ }
+ return OK;
+}
+
+void PreviewFrameScheduler::onNewPresentationTime(const std::vector<nsecs_t>& timeline) {
+ ATRACE_CALL();
+ Mutex::Autolock l(mLock);
+ if (mPendingBuffers.size() > 0) {
+ auto nextBuffer = mPendingBuffers.front();
+ mPendingBuffers.pop();
+
+ // Find the best presentation time by finding the element in the
+ // choreographer timeline that's closest to the ideal presentation time.
+ // The ideal presentation time is the last presentation time + frame
+ // interval.
+ nsecs_t cameraInterval = nextBuffer.timestamp - mLastCameraCaptureTime;
+ nsecs_t idealPresentTime = (cameraInterval < kSpacingResetIntervalNs) ?
+ (mLastCameraPresentTime + cameraInterval) : nextBuffer.timestamp;
+ nsecs_t presentTime = *std::min_element(timeline.begin(), timeline.end(),
+ [idealPresentTime](nsecs_t p1, nsecs_t p2) {
+ return std::abs(p1 - idealPresentTime) < std::abs(p2 - idealPresentTime);
+ });
+
+ status_t res = queueBufferToClientLocked(nextBuffer, presentTime);
+ ATRACE_INT(kPendingBufferTraceName, mPendingBuffers.size());
+
+ if (mParent.shouldLogError(res)) {
+ ALOGE("%s: Preview Stream: Error queueing buffer to native window:"
+ " %s (%d)", __FUNCTION__, strerror(-res), res);
+ }
+
+ mLastCameraCaptureTime = nextBuffer.timestamp;
+ mLastCameraPresentTime = presentTime;
+ }
+}
+
+status_t PreviewFrameScheduler::queueBufferToClientLocked(
+ const BufferHolder& bufferHolder, nsecs_t timestamp) {
+ mParent.setTransform(bufferHolder.transform);
+
+ status_t res = native_window_set_buffers_timestamp(mConsumer.get(), timestamp);
+ if (res != OK) {
+ ALOGE("%s: Preview Stream: Error setting timestamp: %s (%d)",
+ __FUNCTION__, strerror(-res), res);
+ return res;
+ }
+
+ res = mConsumer->queueBuffer(mConsumer.get(), bufferHolder.anwBuffer.get(),
+ bufferHolder.releaseFence);
+ if (res != OK) {
+ close(bufferHolder.releaseFence);
+ }
+
+ return res;
+}
+
+}; // namespace camera3
+
+}; // namespace android
diff --git a/services/camera/libcameraservice/device3/PreviewFrameScheduler.h b/services/camera/libcameraservice/device3/PreviewFrameScheduler.h
new file mode 100644
index 0000000..c0574fd
--- /dev/null
+++ b/services/camera/libcameraservice/device3/PreviewFrameScheduler.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef ANDROID_SERVERS_CAMERA_CAMERA3_PREVIEWFRAMESCHEDULER_H
+#define ANDROID_SERVERS_CAMERA_CAMERA3_PREVIEWFRAMESCHEDULER_H
+
+#include <queue>
+
+#include <android/choreographer.h>
+#include <gui/Surface.h>
+#include <gui/ISurfaceComposer.h>
+#include <utils/Condition.h>
+#include <utils/Mutex.h>
+#include <utils/Looper.h>
+#include <utils/Thread.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+namespace camera3 {
+
+class Camera3OutputStream;
+struct ChoreographerThread;
+
+/***
+ * Preview stream scheduler for better preview display synchronization
+ *
+ * The ideal viewfinder user experience is that frames are presented to the
+ * user in the same cadence as outputed by the camera sensor. However, the
+ * processing latency between frames could vary, due to factors such
+ * as CPU load, differences in request settings, etc. This frame processing
+ * latency results in variation in presentation of frames to the user.
+ *
+ * The PreviewFrameScheduler improves the viewfinder user experience by:
+ * 1. Cache preview buffers in the scheduler
+ * 2. For each choreographer callback, queue the oldest cached buffer with
+ * the best matching presentation timestamp. Frame N's presentation timestamp
+ * is the choreographer timeline timestamp closest to (Frame N-1's
+ * presentation time + camera capture interval between frame N-1 and frame N).
+ * 3. Maintain at most 2 queue-able buffers. If the 3rd preview buffer becomes
+ * available, queue the oldest cached buffer to the buffer queue.
+ */
+class PreviewFrameScheduler {
+ public:
+ explicit PreviewFrameScheduler(Camera3OutputStream& parent, sp<Surface> consumer);
+ virtual ~PreviewFrameScheduler();
+
+ // Queue preview buffer locally
+ status_t queuePreviewBuffer(nsecs_t timestamp, int32_t transform,
+ ANativeWindowBuffer* anwBuffer, int releaseFence);
+
+ // Callback function with a new presentation timeline from choreographer. This
+ // will trigger a locally queued buffer be sent to the buffer queue.
+ void onNewPresentationTime(const std::vector<nsecs_t>& presentationTimeline);
+
+ // Maintain at most 2 queue-able buffers
+ static constexpr int32_t kQueueDepthWatermark = 2;
+
+ private:
+ // structure holding cached preview buffer info
+ struct BufferHolder {
+ nsecs_t timestamp;
+ int32_t transform;
+ sp<ANativeWindowBuffer> anwBuffer;
+ int releaseFence;
+
+ BufferHolder(nsecs_t t, int32_t tr, ANativeWindowBuffer* anwb, int rf) :
+ timestamp(t), transform(tr), anwBuffer(anwb), releaseFence(rf) {}
+ };
+
+ status_t queueBufferToClientLocked(const BufferHolder& bufferHolder,
+ nsecs_t presentTime);
+
+ static constexpr char kPendingBufferTraceName[] = "pending_preview_buffers";
+
+ // Camera capture interval for resetting frame spacing between preview sessions
+ static constexpr nsecs_t kSpacingResetIntervalNs = 1000000000L; // 1 second
+
+ Camera3OutputStream& mParent;
+ sp<ANativeWindow> mConsumer;
+ mutable Mutex mLock;
+
+ std::queue<BufferHolder> mPendingBuffers;
+ nsecs_t mLastCameraCaptureTime = 0;
+ nsecs_t mLastCameraPresentTime = 0;
+
+ // Choreographer related
+ sp<Looper> mLooper;
+ sp<ChoreographerThread> mChoreographerThread;
+};
+
+}; //namespace camera3
+}; //namespace android
+
+#endif
diff --git a/services/tuner/TunerFilter.cpp b/services/tuner/TunerFilter.cpp
index cb903f2..2efbd91 100644
--- a/services/tuner/TunerFilter.cpp
+++ b/services/tuner/TunerFilter.cpp
@@ -453,8 +453,10 @@
void TunerFilter::FilterCallback::detachSharedFilterCallback() {
Mutex::Autolock _l(mCallbackLock);
- mTunerFilterCallback = mOriginalCallback;
- mOriginalCallback = nullptr;
+ if (mTunerFilterCallback != nullptr && mOriginalCallback != nullptr) {
+ mTunerFilterCallback = mOriginalCallback;
+ mOriginalCallback = nullptr;
+ }
}
} // namespace tuner
diff --git a/services/tuner/TunerHelper.h b/services/tuner/TunerHelper.h
index c1252b9..755df57 100644
--- a/services/tuner/TunerHelper.h
+++ b/services/tuner/TunerHelper.h
@@ -43,9 +43,9 @@
typedef enum {
FRONTEND,
- LNB,
DEMUX,
DESCRAMBLER,
+ LNB
} TunerResourceType;
class TunerHelper {
diff --git a/services/tuner/hidl/TunerHidlFilter.cpp b/services/tuner/hidl/TunerHidlFilter.cpp
index dddd025..93d9431 100644
--- a/services/tuner/hidl/TunerHidlFilter.cpp
+++ b/services/tuner/hidl/TunerHidlFilter.cpp
@@ -941,8 +941,10 @@
void TunerHidlFilter::FilterCallback::detachSharedFilterCallback() {
Mutex::Autolock _l(mCallbackLock);
- mTunerFilterCallback = mOriginalCallback;
- mOriginalCallback = nullptr;
+ if (mTunerFilterCallback != nullptr && mOriginalCallback != nullptr) {
+ mTunerFilterCallback = mOriginalCallback;
+ mOriginalCallback = nullptr;
+ }
}
/////////////// FilterCallback Helper Methods ///////////////////////
diff --git a/services/tuner/mediatuner.rc b/services/tuner/mediatuner.rc
index fd30618..6a3e199 100644
--- a/services/tuner/mediatuner.rc
+++ b/services/tuner/mediatuner.rc
@@ -2,4 +2,7 @@
class main
group media
ioprio rt 4
+ onrestart restart vendor.tuner-hal-1-0
+ onrestart restart vendor.tuner-hal-1-1
+ onrestart restart vendor.tuner-default
task_profiles ProcessCapacityHigh HighPerformance