Merge "CodecCapabilities NDK: Introduce AMediaCodecStore and AMediaCodecInfo." into main
diff --git a/media/codec2/components/apv/C2SoftApvEnc.cpp b/media/codec2/components/apv/C2SoftApvEnc.cpp
index 9c5e0b2..9d84bc7 100644
--- a/media/codec2/components/apv/C2SoftApvEnc.cpp
+++ b/media/codec2/components/apv/C2SoftApvEnc.cpp
@@ -222,6 +222,7 @@
.build());
std::vector<uint32_t> pixelFormats = {
HAL_PIXEL_FORMAT_IMPLEMENTATION_DEFINED,
+ HAL_PIXEL_FORMAT_YCBCR_420_888,
};
if (isHalPixelFormatSupported((AHardwareBuffer_Format)HAL_PIXEL_FORMAT_YCBCR_P010)) {
pixelFormats.push_back(HAL_PIXEL_FORMAT_YCBCR_P010);
@@ -231,7 +232,7 @@
}
addParameter(DefineParam(mPixelFormat, C2_PARAMKEY_PIXEL_FORMAT)
.withDefault(new C2StreamPixelFormatInfo::input(
- 0u, HAL_PIXEL_FORMAT_YCBCR_P010))
+ 0u, HAL_PIXEL_FORMAT_YCBCR_420_888))
.withFields({C2F(mPixelFormat, value).oneOf({pixelFormats})})
.withSetter((Setter<decltype(*mPixelFormat)>::StrictValueWithNoDeps))
.build());
@@ -272,6 +273,13 @@
if (!me.F(me.v.level).supportsAtAll(me.v.level)) {
me.set().level = LEVEL_APV_1_BAND_0;
}
+
+ int32_t bandIdc = me.v.level <= LEVEL_APV_7_1_BAND_0 ? 0 :
+ me.v.level <= LEVEL_APV_7_1_BAND_1 ? 1 :
+ me.v.level <= LEVEL_APV_7_1_BAND_2 ? 2 : 3;
+
+ me.set().level = decisionApvLevel(size.v.width, size.v.height, frameRate.v.value,
+ (uint64_t)bitrate.v.value, bandIdc);
return C2R::Ok();
}
@@ -302,6 +310,119 @@
return C2R::Ok();
}
+ static C2Config::level_t decisionApvLevel(int32_t width, int32_t height, int32_t fps,
+ uint64_t bitrate, int32_t band) {
+ C2Config::level_t level = C2Config::LEVEL_APV_1_BAND_0;
+ struct LevelLimits {
+ C2Config::level_t level;
+ uint64_t samplesPerSec;
+ uint64_t kbpsOfBand;
+ };
+
+ constexpr LevelLimits kLimitsBand0[] = {
+ {LEVEL_APV_1_BAND_0, 3'041'280, 7'000},
+ {LEVEL_APV_1_1_BAND_0, 6'082'560, 14'000},
+ {LEVEL_APV_2_BAND_0, 15'667'200, 36'000},
+ {LEVEL_APV_2_1_BAND_0, 31'334'400, 71'000},
+ {LEVEL_APV_3_BAND_0, 66'846'720, 101'000},
+ {LEVEL_APV_3_1_BAND_0, 133'693'440, 201'000},
+ {LEVEL_APV_4_BAND_0, 265'420'800, 401'000},
+ {LEVEL_APV_4_1_BAND_0, 530'841'600, 780'000},
+ {LEVEL_APV_5_BAND_0, 1'061'683'200, 1'560'000},
+ {LEVEL_APV_5_1_BAND_0, 2'123'366'400, 3'324'000},
+ {LEVEL_APV_6_BAND_0, 4'777'574'400, 6'648'000},
+ {LEVEL_APV_6_1_BAND_0, 8'493'465'600, 13'296'000},
+ {LEVEL_APV_7_BAND_0, 16'986'931'200, 26'592'000},
+ {LEVEL_APV_7_1_BAND_0, 33'973'862'400, 53'184'000},
+ };
+
+ constexpr LevelLimits kLimitsBand1[] = {
+ {LEVEL_APV_1_BAND_1, 3'041'280, 11'000},
+ {LEVEL_APV_1_1_BAND_1, 6'082'560, 21'000},
+ {LEVEL_APV_2_BAND_1, 15'667'200, 53'000},
+ {LEVEL_APV_2_1_BAND_1, 31'334'400, 106'00},
+ {LEVEL_APV_3_BAND_1, 66'846'720, 151'000},
+ {LEVEL_APV_3_1_BAND_1, 133'693'440, 301'000},
+ {LEVEL_APV_4_BAND_1, 265'420'800, 602'000},
+ {LEVEL_APV_4_1_BAND_1, 530'841'600, 1'170'000},
+ {LEVEL_APV_5_BAND_1, 1'061'683'200, 2'340'000},
+ {LEVEL_APV_5_1_BAND_1, 2'123'366'400, 4'986'000},
+ {LEVEL_APV_6_BAND_1, 4'777'574'400, 9'972'000},
+ {LEVEL_APV_6_1_BAND_1, 8'493'465'600, 19'944'000},
+ {LEVEL_APV_7_BAND_1, 16'986'931'200, 39'888'000},
+ {LEVEL_APV_7_1_BAND_1, 33'973'862'400, 79'776'000},
+ };
+
+ constexpr LevelLimits kLimitsBand2[] = {
+ {LEVEL_APV_1_BAND_2, 3'041'280, 14'000},
+ {LEVEL_APV_1_1_BAND_2, 6'082'560, 28'000},
+ {LEVEL_APV_2_BAND_2, 15'667'200, 71'000},
+ {LEVEL_APV_2_1_BAND_2, 31'334'400, 141'000},
+ {LEVEL_APV_3_BAND_2, 66'846'720, 201'000},
+ {LEVEL_APV_3_1_BAND_2, 133'693'440, 401'000},
+ {LEVEL_APV_4_BAND_2, 265'420'800, 780'000},
+ {LEVEL_APV_4_1_BAND_2, 530'841'600, 1'560'000},
+ {LEVEL_APV_5_BAND_2, 1'061'683'200, 3'324'000},
+ {LEVEL_APV_5_1_BAND_2, 2'123'366'400, 6'648'000},
+ {LEVEL_APV_6_BAND_2, 4'777'574'400, 13'296'000},
+ {LEVEL_APV_6_1_BAND_2, 8'493'465'600, 26'592'000},
+ {LEVEL_APV_7_BAND_2, 16'986'931'200, 53'184'000},
+ {LEVEL_APV_7_1_BAND_2, 33'973'862'400, 106'368'000},
+ };
+
+ constexpr LevelLimits kLimitsBand3[] = {
+ {LEVEL_APV_1_BAND_3, 3'041'280, 21'000},
+ {LEVEL_APV_1_1_BAND_3, 6'082'560, 42'000},
+ {LEVEL_APV_2_BAND_3, 15'667'200, 106'000},
+ {LEVEL_APV_2_1_BAND_3, 31'334'400, 212'000},
+ {LEVEL_APV_3_BAND_3, 66'846'720, 301'000},
+ {LEVEL_APV_3_1_BAND_3, 133'693'440, 602'000},
+ {LEVEL_APV_4_BAND_3, 265'420'800, 1'170'000},
+ {LEVEL_APV_4_1_BAND_3, 530'841'600, 2'340'000},
+ {LEVEL_APV_5_BAND_3, 1'061'683'200, 4'986'000},
+ {LEVEL_APV_5_1_BAND_3, 2'123'366'400, 9'972'000},
+ {LEVEL_APV_6_BAND_3, 4'777'574'400, 19'944'000},
+ {LEVEL_APV_6_1_BAND_3, 8'493'465'600, 39'888'000},
+ {LEVEL_APV_7_BAND_3, 16'986'931'200, 79'776'000},
+ {LEVEL_APV_7_1_BAND_3, 33'973'862'400, 159'552'000},
+ };
+
+ uint64_t samplesPerSec = width * height * fps;
+ if (band == 0) {
+ for (const LevelLimits& limit : kLimitsBand0) {
+ if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+ level = limit.level;
+ break;
+ }
+ }
+ } else if (band == 1) {
+ for (const LevelLimits& limit : kLimitsBand1) {
+ if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+ level = limit.level;
+ break;
+ }
+ }
+ } else if (band == 2) {
+ for (const LevelLimits& limit : kLimitsBand2) {
+ if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+ level = limit.level;
+ break;
+ }
+ }
+ } else if (band == 3) {
+ for (const LevelLimits& limit : kLimitsBand3) {
+ if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.kbpsOfBand * 1000) {
+ level = limit.level;
+ break;
+ }
+ }
+ } else {
+ ALOGE("Invalid band_idc on calculte level");
+ }
+
+ return level;
+ }
+
uint32_t getProfile_l() const {
int32_t profile = PROFILE_UNUSED;
@@ -328,7 +449,7 @@
profile = 99;
break;
default:
- ALOGD("Unrecognized profile: %x", mProfileLevel->profile);
+ ALOGW("Unrecognized profile: %x", mProfileLevel->profile);
}
return profile;
}
@@ -339,54 +460,264 @@
// TODO: Add Band settings
switch (mProfileLevel->level) {
case C2Config::LEVEL_APV_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_BAND_3:
level = 10;
break;
case C2Config::LEVEL_APV_1_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_3:
level = 11;
break;
case C2Config::LEVEL_APV_2_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_3:
level = 20;
break;
case C2Config::LEVEL_APV_2_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_3:
level = 21;
break;
case C2Config::LEVEL_APV_3_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_3:
level = 30;
break;
case C2Config::LEVEL_APV_3_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_3:
level = 31;
break;
case C2Config::LEVEL_APV_4_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_3:
level = 40;
break;
case C2Config::LEVEL_APV_4_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_3:
level = 41;
break;
case C2Config::LEVEL_APV_5_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_3:
level = 50;
break;
case C2Config::LEVEL_APV_5_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_3:
level = 51;
break;
case C2Config::LEVEL_APV_6_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_3:
level = 60;
break;
case C2Config::LEVEL_APV_6_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_3:
level = 61;
break;
case C2Config::LEVEL_APV_7_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_3:
level = 70;
break;
case C2Config::LEVEL_APV_7_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_3:
level = 71;
break;
default:
- ALOGD("Unrecognized level: %x", mProfileLevel->level);
+ ALOGW("Unrecognized level: %x", mProfileLevel->level);
}
// Convert to APV level_idc according to APV spec
return level * 3;
}
+ uint32_t getBandIdc_l() const {
+ uint32_t bandIdc = 0;
+
+ switch (mProfileLevel->level) {
+ case C2Config::LEVEL_APV_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_0:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_0:
+ bandIdc = 0;
+ break;
+ case C2Config::LEVEL_APV_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_1:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_1:
+ bandIdc = 1;
+ break;
+ case C2Config::LEVEL_APV_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_2:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_2:
+ bandIdc = 2;
+ break;
+ case C2Config::LEVEL_APV_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_1_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_2_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_3_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_4_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_5_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_6_1_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_BAND_3:
+ [[fallthrough]];
+ case C2Config::LEVEL_APV_7_1_BAND_3:
+ bandIdc = 3;
+ break;
+ default:
+ ALOGW("Unrecognized bandIdc through level: %x", mProfileLevel->level);
+ }
+ return bandIdc;
+ }
+
int32_t getBitrateMode_l() const {
int32_t bitrateMode = C2Config::BITRATE_CONST;
@@ -636,7 +967,7 @@
param.h = mSize->height;
param.fps_num = (int)(mFrameRate->value * 100);
param.fps_den = 100;
- param.bitrate = mBitrate->value / 1000;
+ param.bitrate = (int)(mBitrate->value / 1000);
param.rc_type = mIntf->getBitrateMode_l();
int ApvQP = kApvDefaultQP;
@@ -646,14 +977,8 @@
mQuality->value, ApvQP);
}
param.qp = ApvQP;
- param.band_idc = 0; // TODO: Get from the Level setting
+ param.band_idc = mIntf->getBandIdc_l();
param.profile_idc = mIntf->getProfile_l();
- C2Config::level_t level = decisionApvLevel(
- param.w, param.h, (int)(param.fps_num / param.fps_den), param.bitrate, param.band_idc);
- if (mProfileLevel->level != level) {
- mProfileLevel->level = level;
- ALOGI("Need to update level to %d", mIntf->getLevel_l());
- }
param.level_idc = mIntf->getLevel_l();
}
@@ -750,120 +1075,6 @@
return C2_OK;
}
-C2Config::level_t C2SoftApvEnc::decisionApvLevel(int32_t width, int32_t height, int32_t fps,
- int32_t bitrate, int32_t band) {
- C2Config::level_t level = C2Config::LEVEL_APV_1_BAND_0;
-
- struct LevelLimits {
- C2Config::level_t level;
- uint64_t samplesPerSec;
- uint32_t bitratesOfBand;
- };
-
- constexpr LevelLimits kLimitsBand0[] = {
- {LEVEL_APV_1_BAND_0, 3'041'280, 7'000},
- {LEVEL_APV_1_1_BAND_0, 6'082'560, 14'000},
- {LEVEL_APV_2_BAND_0, 15'667'200, 36'000},
- {LEVEL_APV_2_1_BAND_0, 31'334'400, 71'000},
- {LEVEL_APV_3_BAND_0, 66'846'720, 101'000},
- {LEVEL_APV_3_1_BAND_0, 133'693'440, 201'000},
- {LEVEL_APV_4_BAND_0, 265'420'800, 401'000},
- {LEVEL_APV_4_1_BAND_0, 530'841'600, 780'000},
- {LEVEL_APV_5_BAND_0, 1'061'683'200, 1'560'000},
- {LEVEL_APV_5_1_BAND_0, 2'123'366'400, 3'324'000},
- {LEVEL_APV_6_BAND_0, 4'777'574'400, 6'648'000},
- {LEVEL_APV_6_1_BAND_0, 8'493'465'600, 13'296'000},
- {LEVEL_APV_7_BAND_0, 16'986'931'200, 26'592'000},
- {LEVEL_APV_7_1_BAND_0, 33'973'862'400, 53'184'000},
- };
-
- constexpr LevelLimits kLimitsBand1[] = {
- {LEVEL_APV_1_BAND_1, 3'041'280, 11'000},
- {LEVEL_APV_1_1_BAND_1, 6'082'560, 21'000},
- {LEVEL_APV_2_BAND_1, 15'667'200, 53'000},
- {LEVEL_APV_2_1_BAND_1, 31'334'400, 106'00},
- {LEVEL_APV_3_BAND_1, 66'846'720, 151'000},
- {LEVEL_APV_3_1_BAND_1, 133'693'440, 301'000},
- {LEVEL_APV_4_BAND_1, 265'420'800, 602'000},
- {LEVEL_APV_4_1_BAND_1, 530'841'600, 1'170'000},
- {LEVEL_APV_5_BAND_1, 1'061'683'200, 2'340'000},
- {LEVEL_APV_5_1_BAND_1, 2'123'366'400, 4'986'000},
- {LEVEL_APV_6_BAND_1, 4'777'574'400, 9'972'000},
- {LEVEL_APV_6_1_BAND_1, 8'493'465'600, 19'944'000},
- {LEVEL_APV_7_BAND_1, 16'986'931'200, 39'888'000},
- {LEVEL_APV_7_1_BAND_1, 33'973'862'400, 79'776'000},
- };
-
- constexpr LevelLimits kLimitsBand2[] = {
- {LEVEL_APV_1_BAND_2, 3'041'280, 14'000},
- {LEVEL_APV_1_1_BAND_2, 6'082'560, 28'000},
- {LEVEL_APV_2_BAND_2, 15'667'200, 71'000},
- {LEVEL_APV_2_1_BAND_2, 31'334'400, 141'000},
- {LEVEL_APV_3_BAND_2, 66'846'720, 201'000},
- {LEVEL_APV_3_1_BAND_2, 133'693'440, 401'000},
- {LEVEL_APV_4_BAND_2, 265'420'800, 780'000},
- {LEVEL_APV_4_1_BAND_2, 530'841'600, 1'560'000},
- {LEVEL_APV_5_BAND_2, 1'061'683'200, 3'324'000},
- {LEVEL_APV_5_1_BAND_2, 2'123'366'400, 6'648'000},
- {LEVEL_APV_6_BAND_2, 4'777'574'400, 13'296'000},
- {LEVEL_APV_6_1_BAND_2, 8'493'465'600, 26'592'000},
- {LEVEL_APV_7_BAND_2, 16'986'931'200, 53'184'000},
- {LEVEL_APV_7_1_BAND_2, 33'973'862'400, 106'368'000},
- };
-
- constexpr LevelLimits kLimitsBand3[] = {
- {LEVEL_APV_1_BAND_3, 3'041'280, 21'000},
- {LEVEL_APV_1_1_BAND_3, 6'082'560, 42'000},
- {LEVEL_APV_2_BAND_3, 15'667'200, 106'000},
- {LEVEL_APV_2_1_BAND_3, 31'334'400, 212'000},
- {LEVEL_APV_3_BAND_3, 66'846'720, 301'000},
- {LEVEL_APV_3_1_BAND_3, 133'693'440, 602'000},
- {LEVEL_APV_4_BAND_3, 265'420'800, 1'170'000},
- {LEVEL_APV_4_1_BAND_3, 530'841'600, 2'340'000},
- {LEVEL_APV_5_BAND_3, 1'061'683'200, 4'986'000},
- {LEVEL_APV_5_1_BAND_3, 2'123'366'400, 9'972'000},
- {LEVEL_APV_6_BAND_3, 4'777'574'400, 19'944'000},
- {LEVEL_APV_6_1_BAND_3, 8'493'465'600, 39'888'000},
- {LEVEL_APV_7_BAND_3, 16'986'931'200, 79'776'000},
- {LEVEL_APV_7_1_BAND_3, 33'973'862'400, 159'552'000},
- };
-
- uint64_t samplesPerSec = width * height * fps;
- if (band == 0) {
- for (const LevelLimits& limit : kLimitsBand0) {
- if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.bitratesOfBand) {
- level = limit.level;
- break;
- }
- }
- } else if (band == 1) {
- for (const LevelLimits& limit : kLimitsBand1) {
- if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.bitratesOfBand) {
- level = limit.level;
- break;
- }
- }
- } else if (band == 2) {
- for (const LevelLimits& limit : kLimitsBand2) {
- if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.bitratesOfBand) {
- level = limit.level;
- break;
- }
- }
- } else if (band == 3) {
- for (const LevelLimits& limit : kLimitsBand3) {
- if (samplesPerSec <= limit.samplesPerSec && bitrate <= limit.bitratesOfBand) {
- level = limit.level;
- break;
- }
- }
- } else {
- ALOGE("Invalid band_idc on calculte level");
- }
-
- return level;
-}
-
void C2SoftApvEnc::ColorConvertP010ToYUV422P10le(const C2GraphicView* const input,
oapv_imgb_t* imgb) {
uint32_t width = input->width();
diff --git a/media/codec2/components/apv/C2SoftApvEnc.h b/media/codec2/components/apv/C2SoftApvEnc.h
index fc4ad7d..f281052 100644
--- a/media/codec2/components/apv/C2SoftApvEnc.h
+++ b/media/codec2/components/apv/C2SoftApvEnc.h
@@ -60,8 +60,6 @@
const std::unique_ptr<C2Work>& work);
void setParams(oapve_param_t& param);
int32_t getQpFromQuality(int quality);
- C2Config::level_t decisionApvLevel(int32_t width, int32_t height, int32_t fps, int32_t bitrate,
- int32_t band);
void showEncoderParams(oapve_cdesc_t* cdsc);
diff --git a/media/codec2/hal/client/Android.bp b/media/codec2/hal/client/Android.bp
index 864eeb8..029044f 100644
--- a/media/codec2/hal/client/Android.bp
+++ b/media/codec2/hal/client/Android.bp
@@ -23,6 +23,7 @@
name: "libcodec2_client",
srcs: [
+ "ApexCodecsLazy.cpp",
"GraphicBufferAllocator.cpp",
"GraphicsTracker.cpp",
"client.cpp",
@@ -41,17 +42,18 @@
cpp_std: "gnu++20",
header_libs: [
+ "libapexcodecs-header",
"libcodec2_internal", // private
],
shared_libs: [
"android.hardware.graphics.bufferqueue@1.0",
+ "android.hardware.media.bufferpool2-V2-ndk",
"android.hardware.media.bufferpool@2.0",
+ "android.hardware.media.c2-V1-ndk",
"android.hardware.media.c2@1.0",
"android.hardware.media.c2@1.1",
"android.hardware.media.c2@1.2",
- "android.hardware.media.bufferpool2-V2-ndk",
- "android.hardware.media.c2-V1-ndk",
"libbase",
"libbinder",
"libbinder_ndk",
@@ -79,6 +81,10 @@
"include",
],
+ export_header_lib_headers: [
+ "libapexcodecs-header",
+ ],
+
export_shared_lib_headers: [
"android.hardware.media.c2@1.0",
"android.hardware.media.c2@1.1",
@@ -89,5 +95,4 @@
"libcodec2_hidl_client@1.2",
"libcodec2_vndk",
],
-
}
diff --git a/media/codec2/hal/client/ApexCodecsLazy.cpp b/media/codec2/hal/client/ApexCodecsLazy.cpp
new file mode 100644
index 0000000..cd7953e
--- /dev/null
+++ b/media/codec2/hal/client/ApexCodecsLazy.cpp
@@ -0,0 +1,295 @@
+/*
+ * 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.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ApexCodecsLazy"
+#include <log/log.h>
+
+#include <mutex>
+
+#include <dlfcn.h>
+
+#include <android-base/no_destructor.h>
+#include <apex/ApexCodecs.h>
+#include <utils/RWLock.h>
+
+using android::RWLock;
+
+namespace {
+
+// This file provides a lazy interface to libapexcodecs.so to address early boot dependencies.
+
+// Method pointers to libapexcodecs methods are held in an array which simplifies checking
+// all pointers are initialized.
+enum MethodIndex {
+ k_ApexCodec_Component_create,
+ k_ApexCodec_Component_destroy,
+ k_ApexCodec_Component_flush,
+ k_ApexCodec_Component_getConfigurable,
+ k_ApexCodec_Component_process,
+ k_ApexCodec_Component_start,
+ k_ApexCodec_Component_reset,
+ k_ApexCodec_Configurable_config,
+ k_ApexCodec_Configurable_query,
+ k_ApexCodec_Configurable_querySupportedParams,
+ k_ApexCodec_Configurable_querySupportedValues,
+ k_ApexCodec_GetComponentStore,
+ k_ApexCodec_ParamDescriptors_getDescriptor,
+ k_ApexCodec_ParamDescriptors_getIndices,
+ k_ApexCodec_ParamDescriptors_release,
+ k_ApexCodec_SettingResults_getResultAtIndex,
+ k_ApexCodec_SettingResults_release,
+ k_ApexCodec_SupportedValues_getTypeAndValues,
+ k_ApexCodec_SupportedValues_release,
+ k_ApexCodec_Traits_get,
+
+ // Marker for count of methods
+ k_MethodCount
+};
+
+class ApexCodecsLazyLoader {
+public:
+ ApexCodecsLazyLoader() = default;
+
+ static ApexCodecsLazyLoader &Get() {
+ static ::android::base::NoDestructor<ApexCodecsLazyLoader> sLoader;
+ return *sLoader;
+ }
+
+ void *getMethodAt(enum MethodIndex index) {
+ RWLock::AutoRLock l(mLock);
+ if (mInit) {
+ return mMethods[index];
+ } else {
+ mLock.unlock();
+ if (!init()) {
+ return nullptr;
+ }
+ mLock.readLock();
+ return mMethods[index];
+ }
+ }
+
+private:
+ static void* LoadLibapexcodecs(int dlopen_flags) {
+ return dlopen("libapexcodecs.so", dlopen_flags);
+ }
+
+ // Initialization and symbol binding.
+ void bindSymbol_l(void* handle, const char* name, enum MethodIndex index) {
+ void* symbol = dlsym(handle, name);
+ ALOGI_IF(symbol == nullptr, "Failed to find symbol '%s' in libapexcodecs.so: %s",
+ name, dlerror());
+ mMethods[index] = symbol;
+ }
+
+ bool init() {
+ {
+ RWLock::AutoRLock l(mLock);
+ if (mInit) {
+ return true;
+ }
+ }
+ void* handle = LoadLibapexcodecs(RTLD_NOW);
+ if (handle == nullptr) {
+ ALOGI("Failed to load libapexcodecs.so: %s", dlerror());
+ return false;
+ }
+
+ RWLock::AutoWLock l(mLock);
+#undef BIND_SYMBOL
+#define BIND_SYMBOL(name) bindSymbol_l(handle, #name, k_##name);
+ BIND_SYMBOL(ApexCodec_Component_create);
+ BIND_SYMBOL(ApexCodec_Component_destroy);
+ BIND_SYMBOL(ApexCodec_Component_flush);
+ BIND_SYMBOL(ApexCodec_Component_getConfigurable);
+ BIND_SYMBOL(ApexCodec_Component_process);
+ BIND_SYMBOL(ApexCodec_Component_start);
+ BIND_SYMBOL(ApexCodec_Component_reset);
+ BIND_SYMBOL(ApexCodec_Configurable_config);
+ BIND_SYMBOL(ApexCodec_Configurable_query);
+ BIND_SYMBOL(ApexCodec_Configurable_querySupportedParams);
+ BIND_SYMBOL(ApexCodec_Configurable_querySupportedValues);
+ BIND_SYMBOL(ApexCodec_GetComponentStore);
+ BIND_SYMBOL(ApexCodec_ParamDescriptors_getDescriptor);
+ BIND_SYMBOL(ApexCodec_ParamDescriptors_getIndices);
+ BIND_SYMBOL(ApexCodec_ParamDescriptors_release);
+ BIND_SYMBOL(ApexCodec_SettingResults_getResultAtIndex);
+ BIND_SYMBOL(ApexCodec_SettingResults_release);
+ BIND_SYMBOL(ApexCodec_SupportedValues_getTypeAndValues);
+ BIND_SYMBOL(ApexCodec_SupportedValues_release);
+ BIND_SYMBOL(ApexCodec_Traits_get);
+#undef BIND_SYMBOL
+
+ // Check every symbol is bound.
+ for (int i = 0; i < k_MethodCount; ++i) {
+ if (mMethods[i] == nullptr) {
+ ALOGI("Uninitialized method in libapexcodecs_lazy at index: %d", i);
+ return false;
+ }
+ }
+ mInit = true;
+ return true;
+ }
+
+ RWLock mLock;
+ // Table of methods pointers in libapexcodecs APIs.
+ void* mMethods[k_MethodCount];
+ bool mInit{false};
+};
+
+} // anonymous namespace
+
+#define INVOKE_METHOD(name, returnIfNull, args...) \
+ do { \
+ void* method = ApexCodecsLazyLoader::Get().getMethodAt(k_##name); \
+ if (!method) return (returnIfNull); \
+ return reinterpret_cast<decltype(&name)>(method)(args); \
+ } while (0)
+
+//
+// Forwarding for methods in ApexCodecs.h.
+//
+
+ApexCodec_ComponentStore *ApexCodec_GetComponentStore() {
+ INVOKE_METHOD(ApexCodec_GetComponentStore, nullptr);
+}
+
+ApexCodec_ComponentTraits *ApexCodec_Traits_get(
+ ApexCodec_ComponentStore *store, size_t index) {
+ INVOKE_METHOD(ApexCodec_Traits_get, nullptr, store, index);
+}
+
+ApexCodec_Status ApexCodec_Component_create(
+ ApexCodec_ComponentStore *store, const char *name, ApexCodec_Component **comp) {
+ INVOKE_METHOD(ApexCodec_Component_create, APEXCODEC_STATUS_OMITTED, store, name, comp);
+}
+
+void ApexCodec_Component_destroy(ApexCodec_Component *comp) {
+ INVOKE_METHOD(ApexCodec_Component_destroy, void(), comp);
+}
+
+ApexCodec_Status ApexCodec_Component_start(ApexCodec_Component *comp) {
+ INVOKE_METHOD(ApexCodec_Component_start, APEXCODEC_STATUS_OMITTED, comp);
+}
+
+ApexCodec_Status ApexCodec_Component_flush(ApexCodec_Component *comp) {
+ INVOKE_METHOD(ApexCodec_Component_flush, APEXCODEC_STATUS_OMITTED, comp);
+}
+
+ApexCodec_Status ApexCodec_Component_reset(ApexCodec_Component *comp) {
+ INVOKE_METHOD(ApexCodec_Component_reset, APEXCODEC_STATUS_OMITTED, comp);
+}
+
+ApexCodec_Configurable *ApexCodec_Component_getConfigurable(
+ ApexCodec_Component *comp) {
+ INVOKE_METHOD(ApexCodec_Component_getConfigurable, nullptr, comp);
+}
+
+ApexCodec_Status ApexCodec_SupportedValues_getTypeAndValues(
+ ApexCodec_SupportedValues *supportedValues,
+ ApexCodec_SupportedValuesType *type,
+ ApexCodec_SupportedValuesNumberType *numberType,
+ ApexCodec_Value **values,
+ uint32_t *numValues) {
+ INVOKE_METHOD(ApexCodec_SupportedValues_getTypeAndValues, APEXCODEC_STATUS_OMITTED,
+ supportedValues, type, numberType, values, numValues);
+}
+
+void ApexCodec_SupportedValues_release(ApexCodec_SupportedValues *values) {
+ INVOKE_METHOD(ApexCodec_SupportedValues_release, void(), values);
+}
+
+ApexCodec_Status ApexCodec_SettingResults_getResultAtIndex(
+ ApexCodec_SettingResults *results,
+ size_t index,
+ ApexCodec_SettingResultFailure *failure,
+ ApexCodec_ParamFieldValues *field,
+ ApexCodec_ParamFieldValues **conflicts,
+ size_t *numConflicts) {
+ INVOKE_METHOD(ApexCodec_SettingResults_getResultAtIndex, APEXCODEC_STATUS_OMITTED,
+ results, index, failure, field, conflicts, numConflicts);
+}
+
+void ApexCodec_SettingResults_release(ApexCodec_SettingResults *results) {
+ INVOKE_METHOD(ApexCodec_SettingResults_release, void(), results);
+}
+
+ApexCodec_Status ApexCodec_Component_process(
+ ApexCodec_Component *comp,
+ const ApexCodec_Buffer *input,
+ ApexCodec_Buffer *output,
+ size_t *consumed,
+ size_t *produced) {
+ INVOKE_METHOD(ApexCodec_Component_process, APEXCODEC_STATUS_OMITTED,
+ comp, input, output, consumed, produced);
+}
+
+ApexCodec_Status ApexCodec_Configurable_config(
+ ApexCodec_Configurable *comp,
+ ApexCodec_LinearBuffer *config,
+ ApexCodec_SettingResults **results) {
+ INVOKE_METHOD(ApexCodec_Configurable_config, APEXCODEC_STATUS_OMITTED, comp, config, results);
+}
+
+ApexCodec_Status ApexCodec_Configurable_query(
+ ApexCodec_Configurable *comp,
+ uint32_t indices[],
+ size_t numIndices,
+ ApexCodec_LinearBuffer *config,
+ size_t *writtenOrRequested) {
+ INVOKE_METHOD(ApexCodec_Configurable_query, APEXCODEC_STATUS_OMITTED,
+ comp, indices, numIndices, config, writtenOrRequested);
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_getIndices(
+ ApexCodec_ParamDescriptors *descriptors,
+ uint32_t **indices,
+ size_t *numIndices) {
+ INVOKE_METHOD(ApexCodec_ParamDescriptors_getIndices, APEXCODEC_STATUS_OMITTED,
+ descriptors, indices, numIndices);
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_getDescriptor(
+ ApexCodec_ParamDescriptors *descriptors,
+ uint32_t index,
+ ApexCodec_ParamAttribute *attr,
+ const char **name,
+ uint32_t **dependencies,
+ size_t *numDependencies) {
+ INVOKE_METHOD(ApexCodec_ParamDescriptors_getDescriptor, APEXCODEC_STATUS_OMITTED,
+ descriptors, index, attr, name, dependencies, numDependencies);
+}
+
+ApexCodec_Status ApexCodec_ParamDescriptors_release(
+ ApexCodec_ParamDescriptors *descriptors) {
+ INVOKE_METHOD(ApexCodec_ParamDescriptors_release, APEXCODEC_STATUS_OMITTED, descriptors);
+}
+
+ApexCodec_Status ApexCodec_Configurable_querySupportedParams(
+ ApexCodec_Configurable *comp,
+ ApexCodec_ParamDescriptors **descriptors) {
+ INVOKE_METHOD(ApexCodec_Configurable_querySupportedParams, APEXCODEC_STATUS_OMITTED,
+ comp, descriptors);
+}
+
+ApexCodec_Status ApexCodec_Configurable_querySupportedValues(
+ ApexCodec_Configurable *comp,
+ ApexCodec_SupportedValuesQuery *queries,
+ size_t numQueries) {
+ INVOKE_METHOD(ApexCodec_Configurable_querySupportedValues, APEXCODEC_STATUS_OMITTED,
+ comp, queries, numQueries);
+}
diff --git a/media/codec2/hal/client/client.cpp b/media/codec2/hal/client/client.cpp
index 6348e42..9ee9b9e 100644
--- a/media/codec2/hal/client/client.cpp
+++ b/media/codec2/hal/client/client.cpp
@@ -20,6 +20,8 @@
#include <android-base/logging.h>
#include <utils/Trace.h>
+#include <android_media_codec.h>
+
#include <codec2/aidl/GraphicBufferAllocator.h>
#include <codec2/common/HalSelection.h>
#include <codec2/hidl/client.h>
@@ -55,7 +57,9 @@
#include <android/binder_ibinder.h>
#include <android/binder_manager.h>
#include <android-base/properties.h>
+#include <android-base/scopeguard.h>
#include <android-base/stringprintf.h>
+#include <apex/ApexCodecs.h>
#include <bufferpool/ClientManager.h>
#include <bufferpool2/ClientManager.h>
#include <codec2/aidl/BufferTypes.h>
@@ -64,14 +68,14 @@
#include <codec2/hidl/1.1/types.h>
#include <codec2/hidl/1.2/types.h>
#include <codec2/hidl/output.h>
-
#include <cutils/native_handle.h>
#include <gui/bufferqueue/2.0/B2HGraphicBufferProducer.h>
#include <gui/bufferqueue/2.0/H2BGraphicBufferProducer.h>
#include <hardware/gralloc.h> // for GRALLOC_USAGE_*
#include <hidl/HidlSupport.h>
-#include <system/window.h> // for NATIVE_WINDOW_QUERY_*
#include <media/stagefright/foundation/ADebug.h> // for asString(status_t)
+#include <private/android/AHardwareBufferHelpers.h>
+#include <system/window.h> // for NATIVE_WINDOW_QUERY_*
#include <deque>
#include <iterator>
@@ -799,6 +803,386 @@
return status;
}
+// Codec2ConfigurableClient::ApexImpl
+
+struct Codec2ConfigurableClient::ApexImpl : public Codec2ConfigurableClient::ImplBase {
+ ApexImpl(ApexCodec_Configurable *base, const C2String &name);
+
+ const C2String& getName() const override {
+ return mName;
+ }
+
+ c2_status_t query(
+ const std::vector<C2Param*>& stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const override;
+
+ c2_status_t config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) override;
+
+ c2_status_t querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params
+ ) const override;
+
+ c2_status_t querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ c2_blocking_t mayBlock) const override;
+
+private:
+ ApexCodec_Configurable* mBase;
+ const C2String mName;
+};
+
+Codec2ConfigurableClient::ApexImpl::ApexImpl(ApexCodec_Configurable *base, const C2String &name)
+ : mBase{base},
+ mName{name} {
+}
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::query(
+ const std::vector<C2Param*> &stackParams,
+ const std::vector<C2Param::Index> &heapParamIndices,
+ [[maybe_unused]] c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2Param>>* const heapParams) const {
+ if (mBase == nullptr) {
+ return C2_OMITTED;
+ }
+
+ if (__builtin_available(android 36, *)) {
+ std::vector<uint32_t> indices(
+ stackParams.size() + heapParamIndices.size());
+ size_t numIndices = 0;
+ for (C2Param* const& stackParam : stackParams) {
+ if (!stackParam) {
+ LOG(WARNING) << "query -- null stack param encountered.";
+ continue;
+ }
+ indices[numIndices++] = uint32_t(stackParam->index());
+ }
+ size_t numStackIndices = numIndices;
+ for (const C2Param::Index& index : heapParamIndices) {
+ indices[numIndices++] = uint32_t(index);
+ }
+ indices.resize(numIndices);
+ if (heapParams) {
+ heapParams->reserve(heapParams->size() + numIndices);
+ }
+ if (numIndices == 0) {
+ return C2_OK;
+ }
+ thread_local std::vector<uint8_t> configBuffer(1024);
+ if (configBuffer.capacity() < numIndices * 16u) {
+ configBuffer.resize(numIndices * 16u);
+ }
+ ApexCodec_LinearBuffer config{configBuffer.data(), configBuffer.capacity()};
+ size_t writtenOrRequested = 0;
+ ApexCodec_Status status = ApexCodec_Configurable_query(
+ mBase, indices.data(), indices.size(), &config, &writtenOrRequested);
+ if (status == APEXCODEC_STATUS_NO_MEMORY) {
+ size_t requested = writtenOrRequested;
+ configBuffer.resize(align(requested, 1024));
+ config.data = configBuffer.data();
+ config.size = configBuffer.capacity();
+ status = ApexCodec_Configurable_query(
+ mBase, indices.data(), indices.size(), &config, &writtenOrRequested);
+ }
+ size_t written = writtenOrRequested;
+ if (status != APEXCODEC_STATUS_OK && status != APEXCODEC_STATUS_BAD_INDEX) {
+ written = 0;
+ }
+ configBuffer.resize(written);
+ std::vector<C2Param*> paramPointers;
+ if (!::android::parseParamsBlob(¶mPointers, configBuffer)) {
+ LOG(ERROR) << "query -- error while parsing params.";
+ return C2_CORRUPTED;
+ }
+ size_t i = 0;
+ size_t numQueried = 0;
+ for (auto it = paramPointers.begin(); it != paramPointers.end(); ) {
+ C2Param* paramPointer = *it;
+ if (numStackIndices > 0) {
+ --numStackIndices;
+ if (!paramPointer) {
+ LOG(DEBUG) << "query -- null stack param.";
+ ++it;
+ continue;
+ }
+ for (; i < stackParams.size() && !stackParams[i]; ) {
+ ++i;
+ }
+ if (i >= stackParams.size()) {
+ LOG(ERROR) << "query -- unexpected error.";
+ status = APEXCODEC_STATUS_CORRUPTED;
+ break;
+ }
+ if (stackParams[i]->index() != paramPointer->index()) {
+ LOG(DEBUG) << "query -- param skipped: "
+ "index = "
+ << stackParams[i]->index() << ".";
+ stackParams[i++]->invalidate();
+ // this means that the param could not be queried.
+ // signalling C2_BAD_INDEX to the client.
+ status = APEXCODEC_STATUS_BAD_INDEX;
+ continue;
+ }
+ if (stackParams[i++]->updateFrom(*paramPointer)) {
+ ++numQueried;
+ } else {
+ LOG(WARNING) << "query -- param update failed: "
+ "index = "
+ << paramPointer->index() << ".";
+ }
+ } else {
+ if (!paramPointer) {
+ LOG(DEBUG) << "query -- null heap param.";
+ ++it;
+ continue;
+ }
+ if (!heapParams) {
+ LOG(WARNING) << "query -- "
+ "unexpected extra stack param.";
+ } else {
+ heapParams->emplace_back(C2Param::Copy(*paramPointer));
+ ++numQueried;
+ }
+ }
+ ++it;
+ }
+ if (status == APEXCODEC_STATUS_OK && indices.size() != numQueried) {
+ status = APEXCODEC_STATUS_BAD_INDEX;
+ }
+ return (c2_status_t)status;
+ } else {
+ return C2_OMITTED;
+ }
+}
+
+namespace {
+struct ParamOrField : public C2ParamField {
+ explicit ParamOrField(const ApexCodec_ParamFieldValues& field)
+ : C2ParamField(field.index, field.offset, field.size) {}
+};
+
+static bool FromApex(
+ ApexCodec_SupportedValues *apexValues,
+ C2FieldSupportedValues* c2Values) {
+ if (__builtin_available(android 36, *)) {
+ if (apexValues == nullptr) {
+ c2Values->type = C2FieldSupportedValues::EMPTY;
+ return true;
+ }
+ ApexCodec_SupportedValuesType type = APEXCODEC_SUPPORTED_VALUES_EMPTY;
+ ApexCodec_SupportedValuesNumberType numberType = APEXCODEC_SUPPORTED_VALUES_TYPE_NONE;
+ ApexCodec_Value* values = nullptr;
+ uint32_t numValues = 0;
+ ApexCodec_SupportedValues_getTypeAndValues(
+ apexValues, &type, &numberType, &values, &numValues);
+ c2Values->type = (C2FieldSupportedValues::type_t)type;
+ std::function<C2Value::Primitive(const ApexCodec_Value &)> getPrimitive;
+ switch (numberType) {
+ case APEXCODEC_SUPPORTED_VALUES_TYPE_NONE:
+ getPrimitive = [](const ApexCodec_Value &) -> C2Value::Primitive {
+ return C2Value::Primitive();
+ };
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_TYPE_INT32:
+ getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+ return C2Value::Primitive(value.i32);
+ };
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_TYPE_UINT32:
+ getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+ return C2Value::Primitive(value.u32);
+ };
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_TYPE_INT64:
+ getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+ return C2Value::Primitive(value.i64);
+ };
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_TYPE_UINT64:
+ getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+ return C2Value::Primitive(value.u64);
+ };
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_TYPE_FLOAT:
+ getPrimitive = [](const ApexCodec_Value &value) -> C2Value::Primitive {
+ return C2Value::Primitive(value.f);
+ };
+ break;
+ default:
+ LOG(ERROR) << "Unsupported number type: " << numberType;
+ return false;
+ }
+ switch (type) {
+ case APEXCODEC_SUPPORTED_VALUES_EMPTY:
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_RANGE:
+ c2Values->range.min = getPrimitive(values[0]);
+ c2Values->range.max = getPrimitive(values[1]);
+ c2Values->range.step = getPrimitive(values[2]);
+ c2Values->range.num = getPrimitive(values[3]);
+ c2Values->range.denom = getPrimitive(values[4]);
+ break;
+ case APEXCODEC_SUPPORTED_VALUES_VALUES:
+ case APEXCODEC_SUPPORTED_VALUES_FLAGS:
+ c2Values->values.clear();
+ for (uint32_t i = 0; i < numValues; ++i) {
+ c2Values->values.push_back(getPrimitive(values[i]));
+ }
+ break;
+ default:
+ LOG(ERROR) << "Unsupported supported values type: " << type;
+ return false;
+ }
+ return true;
+ } else {
+ return false;
+ }
+}
+
+} // anonymous namespace
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::config(
+ const std::vector<C2Param*> ¶ms,
+ c2_blocking_t mayBlock,
+ std::vector<std::unique_ptr<C2SettingResult>>* const failures) {
+ (void)mayBlock;
+ if (mBase == nullptr) {
+ return C2_OMITTED;
+ }
+
+ if (__builtin_available(android 36, *)) {
+ std::vector<uint8_t> configBuffer;
+ if (!::android::_createParamsBlob(&configBuffer, params)) {
+ LOG(ERROR) << "config -- bad input.";
+ return C2_TRANSACTION_FAILED;
+ }
+ ApexCodec_SettingResults* result = nullptr;
+ ApexCodec_LinearBuffer config{configBuffer.data(), configBuffer.size()};
+ ApexCodec_Status status = ApexCodec_Configurable_config(
+ mBase, &config, &result);
+ base::ScopeGuard guard([result] {
+ if (result) {
+ ApexCodec_SettingResults_release(result);
+ }
+ });
+ size_t index = 0;
+ ApexCodec_SettingResultFailure failure;
+ ApexCodec_ParamFieldValues field;
+ ApexCodec_ParamFieldValues* conflicts = nullptr;
+ size_t numConflicts = 0;
+ ApexCodec_Status getResultStatus = ApexCodec_SettingResults_getResultAtIndex(
+ result, 0, &failure, &field, &conflicts, &numConflicts);
+ while (getResultStatus == APEXCODEC_STATUS_OK) {
+ std::unique_ptr<C2SettingResult> settingResult;
+ settingResult.reset(new C2SettingResult{
+ C2SettingResult::Failure(failure), C2ParamFieldValues(ParamOrField(field)), {}
+ });
+ // TODO: settingResult->field.values = ?
+ for (size_t i = 0; i < numConflicts; ++i) {
+ settingResult->conflicts.emplace_back(ParamOrField(conflicts[i]));
+ C2ParamFieldValues& conflict = settingResult->conflicts.back();
+ conflict.values = std::make_unique<C2FieldSupportedValues>();
+ FromApex(conflicts[i].values, conflict.values.get());
+ }
+ failures->push_back(std::move(settingResult));
+ getResultStatus = ApexCodec_SettingResults_getResultAtIndex(
+ result, ++index, &failure, &field, &conflicts, &numConflicts);
+ }
+ if (!::android::updateParamsFromBlob(params, configBuffer)) {
+ LOG(ERROR) << "config -- "
+ << "failed to parse returned params.";
+ status = APEXCODEC_STATUS_CORRUPTED;
+ }
+ return (c2_status_t)status;
+ } else {
+ return C2_OMITTED;
+ }
+}
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::querySupportedParams(
+ std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const {
+ if (mBase == nullptr) {
+ return C2_OMITTED;
+ }
+
+ if (__builtin_available(android 36, *)) {
+ // TODO: Cache and query properly!
+ ApexCodec_ParamDescriptors* paramDescs = nullptr;
+ ApexCodec_Configurable_querySupportedParams(mBase, ¶mDescs);
+ base::ScopeGuard guard([paramDescs] {
+ if (paramDescs) {
+ ApexCodec_ParamDescriptors_release(paramDescs);
+ }
+ });
+ uint32_t *indices = nullptr;
+ size_t numIndices = 0;
+ ApexCodec_Status status = ApexCodec_ParamDescriptors_getIndices(
+ paramDescs, &indices, &numIndices);
+ if (status != APEXCODEC_STATUS_OK) {
+ return (c2_status_t)status;
+ }
+ if (numIndices > 0) {
+ for (int i = 0; i < numIndices; ++i) {
+ uint32_t index = indices[i];
+ ApexCodec_ParamAttribute attr = (ApexCodec_ParamAttribute)0;
+ const char* name = nullptr;
+ uint32_t* dependencies = nullptr;
+ size_t numDependencies = 0;
+ ApexCodec_Status status = ApexCodec_ParamDescriptors_getDescriptor(
+ paramDescs, index, &attr, &name, &dependencies, &numDependencies);
+ if (status != APEXCODEC_STATUS_OK) {
+ LOG(WARNING) << "querySupportedParams -- "
+ << "failed to get descriptor for index "
+ << std::hex << index << std::dec << " with status " << status;
+ continue;
+ }
+ params->push_back(std::make_shared<C2ParamDescriptor>(
+ C2Param::Index(index), C2ParamDescriptor::attrib_t(attr), name,
+ std::vector<C2Param::Index>(dependencies, dependencies + numDependencies)));
+ }
+ }
+ return (c2_status_t)status;
+ } else {
+ return C2_OMITTED;
+ }
+}
+
+c2_status_t Codec2ConfigurableClient::ApexImpl::querySupportedValues(
+ std::vector<C2FieldSupportedValuesQuery>& fields,
+ [[maybe_unused]] c2_blocking_t mayBlock) const {
+ if (mBase == nullptr) {
+ return C2_OMITTED;
+ }
+
+ if (__builtin_available(android 36, *)) {
+ std::vector<ApexCodec_SupportedValuesQuery> queries(fields.size());
+ for (size_t i = 0; i < fields.size(); ++i) {
+ queries[i].index = _C2ParamInspector::GetIndex(fields[i].field());
+ queries[i].offset = _C2ParamInspector::GetOffset(fields[i].field());
+ queries[i].type = (ApexCodec_SupportedValuesQueryType)fields[i].type();
+ queries[i].status = APEXCODEC_STATUS_OK;
+ queries[i].values = nullptr;
+ }
+ ApexCodec_Status status = ApexCodec_Configurable_querySupportedValues(
+ mBase, queries.data(), queries.size());
+ for (size_t i = 0; i < fields.size(); ++i) {
+ fields[i].status = (c2_status_t)queries[i].status;
+ FromApex(queries[i].values, &fields[i].values);
+ if (queries[i].values) {
+ ApexCodec_SupportedValues_release(queries[i].values);
+ queries[i].values = nullptr;
+ }
+ }
+ return (c2_status_t)status;
+ } else {
+ return C2_OMITTED;
+ }
+}
+
// Codec2ConfigurableClient
Codec2ConfigurableClient::Codec2ConfigurableClient(const sp<HidlBase> &hidlBase)
@@ -810,6 +1194,11 @@
: mImpl(new Codec2ConfigurableClient::AidlImpl(aidlBase)) {
}
+Codec2ConfigurableClient::Codec2ConfigurableClient(
+ ApexCodec_Configurable *apexBase, const C2String &name)
+ : mImpl(new Codec2ConfigurableClient::ApexImpl(apexBase, name)) {
+}
+
const C2String& Codec2ConfigurableClient::getName() const {
return mImpl->getName();
}
@@ -1035,6 +1424,393 @@
};
+// Codec2Client::Component::ApexHandler
+class Codec2Client::Component::ApexHandler {
+public:
+ ApexHandler(ApexCodec_Component *apexComponent,
+ const std::shared_ptr<Listener> &listener,
+ const std::shared_ptr<Component> &comp)
+ : mApexComponent(apexComponent),
+ mListener(listener),
+ mComponent(comp),
+ mStopped(false),
+ mOutputBufferType(APEXCODEC_BUFFER_TYPE_INVALID) {
+ }
+
+ void start() {
+ std::shared_ptr<Component> comp = mComponent.lock();
+ if (!comp) {
+ LOG(ERROR) << "ApexHandler::start -- component died.";
+ return;
+ }
+ C2ComponentDomainSetting domain;
+ C2ComponentKindSetting kind;
+ c2_status_t status = comp->query({&domain, &kind}, {}, C2_MAY_BLOCK, {});
+ if (status != C2_OK) {
+ LOG(ERROR) << "ApexHandler::start -- failed to query component domain and kind";
+ return;
+ }
+ if (kind.value != C2Component::KIND_DECODER
+ && kind.value != C2Component::KIND_ENCODER) {
+ LOG(ERROR) << "ApexHandler::start -- unrecognized component kind " << kind.value;
+ return;
+ }
+ ApexCodec_BufferType outputBufferType = APEXCODEC_BUFFER_TYPE_INVALID;
+ if (domain.value == C2Component::DOMAIN_AUDIO) {
+ // For both encoders and decoders the output buffer type is linear.
+ outputBufferType = APEXCODEC_BUFFER_TYPE_LINEAR;
+ } else if (domain.value == C2Component::DOMAIN_VIDEO
+ || domain.value == C2Component::DOMAIN_IMAGE) {
+ // For video / image domain the decoder outputs a graphic buffer, and the encoder
+ // outputs a linear buffer.
+ outputBufferType = (kind.value == C2Component::KIND_DECODER)
+ ? APEXCODEC_BUFFER_TYPE_GRAPHIC : APEXCODEC_BUFFER_TYPE_LINEAR;
+ } else {
+ LOG(ERROR) << "ApexHandler::start -- unrecognized component domain " << domain.value;
+ return;
+ }
+ {
+ std::unique_lock<std::mutex> l(mMutex);
+ mStopped = false;
+ mOutputBufferType = outputBufferType;
+ }
+ mThread = std::thread([this]() {
+ run();
+ });
+ }
+
+ void queue(std::list<std::unique_ptr<C2Work>>& workItems) {
+ std::unique_lock<std::mutex> l(mMutex);
+ mWorkQueue.splice(mWorkQueue.end(), workItems);
+ mCondition.notify_all();
+ }
+
+ void stop() {
+ std::unique_lock<std::mutex> l(mMutex);
+ mStopped = true;
+ mCondition.notify_all();
+ l.unlock();
+ mThread.join();
+ }
+
+private:
+ void run() {
+ while (true) {
+ std::unique_lock<std::mutex> l(mMutex);
+ mCondition.wait(l, [this]() {
+ return !mWorkQueue.empty() || mStopped;
+ });
+ if (mStopped) {
+ break;
+ }
+ if (mWorkQueue.empty()) {
+ continue;
+ }
+ std::list<std::unique_ptr<C2Work>> workItems;
+ mWorkQueue.swap(workItems);
+ for (std::unique_ptr<C2Work>& workItem : workItems) {
+ if (mStopped) {
+ break;
+ }
+ l.unlock();
+ handleWork(std::move(workItem));
+ l.lock();
+ }
+ }
+ mWorkQueue.clear();
+ mWorkMap.clear();
+ }
+
+ void handleWork(std::unique_ptr<C2Work> &&workItem) {
+ if (__builtin_available(android 36, *)) {
+ std::shared_ptr<Listener> listener = mListener.lock();
+ if (!listener) {
+ LOG(DEBUG) << "handleWork -- listener died.";
+ return;
+ }
+ ApexCodec_Buffer input;
+ input.flags = (ApexCodec_BufferFlags)workItem->input.flags;
+ input.frameIndex = workItem->input.ordinal.frameIndex.peekll();
+ input.timestampUs = workItem->input.ordinal.timestamp.peekll();
+
+ if (workItem->input.buffers.size() > 1) {
+ LOG(ERROR) << "handleWork -- input buffer size is "
+ << workItem->input.buffers.size();
+ return;
+ }
+ std::shared_ptr<C2Buffer> buffer;
+ std::optional<C2ReadView> linearView;
+ if (!workItem->input.buffers.empty()) {
+ buffer = workItem->input.buffers[0];
+ }
+ if (!FillMemory(buffer, &input, &linearView)) {
+ LOG(ERROR) << "handleWork -- failed to map input";
+ return;
+ }
+
+ std::vector<uint8_t> configUpdatesVector;
+ if (!_createParamsBlob(&configUpdatesVector, workItem->input.configUpdate)) {
+ listener->onError(mComponent, C2_CORRUPTED);
+ return;
+ }
+ input.configUpdates.data = configUpdatesVector.data();
+ input.configUpdates.size = configUpdatesVector.size();
+ mWorkMap.insert_or_assign(
+ workItem->input.ordinal.frameIndex.peekll(), std::move(workItem));
+
+ std::list<std::unique_ptr<C2Work>> workItems;
+ bool inputDrained = false;
+ while (!inputDrained) {
+ ApexCodec_Buffer output;
+ std::shared_ptr<C2LinearBlock> linearBlock;
+ std::optional<C2WriteView> linearView;
+ std::shared_ptr<C2GraphicBlock> graphicBlock;
+ allocOutputBuffer(&output, &linearBlock, &linearView, &graphicBlock);
+ size_t consumed = 0;
+ size_t produced = 0;
+ ApexCodec_Status status = ApexCodec_Component_process(
+ mApexComponent, &input, &output, &consumed, &produced);
+ if (status == APEXCODEC_STATUS_NO_MEMORY) {
+ continue;
+ }
+ if (produced > 0) {
+ auto it = mWorkMap.find(output.frameIndex);
+ std::unique_ptr<C2Work> outputWorkItem;
+ if (it != mWorkMap.end()) {
+ if (output.flags & APEXCODEC_FLAG_INCOMPLETE) {
+ outputWorkItem = std::make_unique<C2Work>();
+ outputWorkItem->input.ordinal = it->second->input.ordinal;
+ outputWorkItem->input.flags = it->second->input.flags;
+ } else {
+ outputWorkItem = std::move(it->second);
+ mWorkMap.erase(it);
+ }
+ } else {
+ LOG(WARNING) << "handleWork -- no work item found for output frame index "
+ << output.frameIndex;
+ outputWorkItem = std::make_unique<C2Work>();
+ outputWorkItem->input.ordinal.frameIndex = output.frameIndex;
+ outputWorkItem->input.ordinal.timestamp = output.timestampUs;
+ }
+ outputWorkItem->worklets.emplace_back(new C2Worklet);
+ const std::unique_ptr<C2Worklet> &worklet = outputWorkItem->worklets.front();
+ if (worklet == nullptr) {
+ LOG(ERROR) << "handleWork -- output work item has null worklet";
+ return;
+ }
+ worklet->output.ordinal.frameIndex = output.frameIndex;
+ worklet->output.ordinal.timestamp = output.timestampUs;
+ // non-owning hidl_vec<> to wrap around the output config updates
+ hidl_vec<uint8_t> outputConfigUpdates;
+ outputConfigUpdates.setToExternal(
+ output.configUpdates.data, output.configUpdates.size);
+ std::vector<C2Param*> outputConfigUpdatePtrs;
+ parseParamsBlob(&outputConfigUpdatePtrs, outputConfigUpdates);
+ worklet->output.configUpdate.clear();
+ std::ranges::transform(
+ outputConfigUpdatePtrs,
+ std::back_inserter(worklet->output.configUpdate),
+ [](C2Param* param) { return C2Param::Copy(*param); });
+ worklet->output.flags = (C2FrameData::flags_t)output.flags;
+
+ workItems.push_back(std::move(outputWorkItem));
+ }
+
+ // determine whether the input buffer is drained
+ if (input.type == APEXCODEC_BUFFER_TYPE_LINEAR) {
+ if (input.memory.linear.size < consumed) {
+ LOG(WARNING) << "handleWork -- component consumed more bytes "
+ << "than the input buffer size";
+ inputDrained = true;
+ } else {
+ input.memory.linear.data += consumed;
+ input.memory.linear.size -= consumed;
+ }
+ } else if (input.type == APEXCODEC_BUFFER_TYPE_GRAPHIC) {
+ inputDrained = (consumed > 0);
+ }
+ }
+
+ if (!workItems.empty()) {
+ listener->onWorkDone(mComponent, workItems);
+ }
+ }
+ }
+
+ bool ensureBlockPool() {
+ std::shared_ptr<Component> comp = mComponent.lock();
+ if (!comp) {
+ return false;
+ }
+ std::vector<std::unique_ptr<C2Param>> heapParams;
+ comp->query({}, {C2PortBlockPoolsTuning::output::PARAM_TYPE}, C2_MAY_BLOCK, &heapParams);
+ if (heapParams.size() != 1) {
+ return false;
+ }
+ const C2Param* param = heapParams[0].get();
+ if (param->type() != C2PortBlockPoolsTuning::output::PARAM_TYPE) {
+ return false;
+ }
+ const C2PortBlockPoolsTuning::output *blockPools =
+ static_cast<const C2PortBlockPoolsTuning::output *>(param);
+ if (blockPools->flexCount() == 0) {
+ return false;
+ }
+ C2BlockPool::local_id_t blockPoolId = blockPools->m.values[0];
+ if (mBlockPool && mBlockPool->getLocalId() == blockPoolId) {
+ // no need to update
+ return true;
+ }
+ return C2_OK == GetCodec2BlockPool(blockPoolId, nullptr, &mBlockPool);
+ }
+
+ void allocOutputBuffer(
+ ApexCodec_Buffer* output,
+ std::shared_ptr<C2LinearBlock> *linearBlock,
+ std::optional<C2WriteView> *linearView,
+ std::shared_ptr<C2GraphicBlock> *graphicBlock) {
+ if (mOutputBufferType == APEXCODEC_BUFFER_TYPE_LINEAR) {
+ if (!ensureBlockPool()) {
+ return;
+ }
+ {
+ std::shared_ptr<Component> comp = mComponent.lock();
+ if (!comp) {
+ return;
+ }
+ C2StreamMaxBufferSizeInfo::output maxBufferSize(0u /* stream */);
+ comp->query({&maxBufferSize}, {}, C2_MAY_BLOCK, {});
+ mLinearBlockCapacity = maxBufferSize ? maxBufferSize.value : 1024 * 1024;
+ }
+ output->type = APEXCODEC_BUFFER_TYPE_LINEAR;
+ c2_status_t status = mBlockPool->fetchLinearBlock(
+ mLinearBlockCapacity,
+ C2MemoryUsage(C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE),
+ linearBlock);
+ if (!(*linearBlock)) {
+ return;
+ }
+ linearView->emplace((*linearBlock)->map().get());
+ if ((*linearView)->error() != C2_OK) {
+ return;
+ }
+ output->memory.linear.data = (*linearView)->data();
+ output->memory.linear.size = (*linearView)->capacity();
+ } else if (mOutputBufferType == APEXCODEC_BUFFER_TYPE_GRAPHIC) {
+ if (!ensureBlockPool()) {
+ return;
+ }
+ {
+ std::shared_ptr<Component> comp = mComponent.lock();
+ if (!comp) {
+ return;
+ }
+ C2StreamMaxPictureSizeTuning::output maxPictureSize(0u /* stream */);
+ C2StreamPictureSizeInfo::output pictureSize(0u /* stream */);
+ C2StreamPixelFormatInfo::output pixelFormat(0u /* stream */);
+ comp->query({&maxPictureSize, &pictureSize, &pixelFormat}, {}, C2_MAY_BLOCK, {});
+ mWidth = maxPictureSize ? maxPictureSize.width : pictureSize.width;
+ mHeight = maxPictureSize ? maxPictureSize.height : pictureSize.height;
+ mFormat = pixelFormat ? pixelFormat.value : HAL_PIXEL_FORMAT_YCBCR_420_888;
+ }
+ output->type = APEXCODEC_BUFFER_TYPE_GRAPHIC;
+ c2_status_t status = mBlockPool->fetchGraphicBlock(
+ mWidth, mHeight, mFormat,
+ C2MemoryUsage(C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE),
+ graphicBlock);
+ if (!(*graphicBlock)) {
+ return;
+ }
+ const C2Handle *handle = (*graphicBlock)->handle();
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ _UnwrapNativeCodec2GrallocMetadata(
+ handle, &width, &height, &format, &usage, &stride, &generation,
+ &igbp_id, &igbp_slot);
+ native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(handle);
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ grallocHandle, GraphicBuffer::CLONE_HANDLE,
+ width, height, format, 1, usage, stride);
+ native_handle_delete(grallocHandle);
+ AHardwareBuffer *hardwareBuffer =
+ AHardwareBuffer_from_GraphicBuffer(graphicBuffer.get());
+ AHardwareBuffer_acquire(hardwareBuffer);
+ output->memory.graphic = hardwareBuffer;
+ } else {
+ LOG(ERROR) << "allocOutputBuffer -- unsupported output buffer type: "
+ << mOutputBufferType;
+ return;
+ }
+ }
+
+ static bool FillMemory(
+ const std::shared_ptr<C2Buffer>& buffer,
+ ApexCodec_Buffer* apexBuffer,
+ std::optional<C2ReadView>* linearView) {
+ if (buffer->data().type() == C2BufferData::LINEAR) {
+ apexBuffer->type = APEXCODEC_BUFFER_TYPE_LINEAR;
+ if (buffer->data().linearBlocks().empty()) {
+ apexBuffer->memory.linear.data = nullptr;
+ apexBuffer->memory.linear.size = 0;
+ return true;
+ } else if (buffer->data().linearBlocks().size() > 1) {
+ return false;
+ }
+ linearView->emplace(buffer->data().linearBlocks().front().map().get());
+ if ((*linearView)->error() != C2_OK) {
+ return false;
+ }
+ apexBuffer->memory.linear.data = const_cast<uint8_t*>((*linearView)->data());
+ apexBuffer->memory.linear.size = (*linearView)->capacity();
+ return true;
+ } else if (buffer->data().type() == C2BufferData::GRAPHIC) {
+ apexBuffer->type = APEXCODEC_BUFFER_TYPE_GRAPHIC;
+ if (buffer->data().graphicBlocks().empty()) {
+ apexBuffer->memory.graphic = nullptr;
+ return true;
+ } else if (buffer->data().graphicBlocks().size() > 1) {
+ return false;
+ }
+ const C2Handle *handle = buffer->data().graphicBlocks().front().handle();
+ uint32_t width, height, format, stride, igbp_slot, generation;
+ uint64_t usage, igbp_id;
+ _UnwrapNativeCodec2GrallocMetadata(
+ handle, &width, &height, &format, &usage, &stride, &generation,
+ &igbp_id, &igbp_slot);
+ native_handle_t *grallocHandle = UnwrapNativeCodec2GrallocHandle(handle);
+ sp<GraphicBuffer> graphicBuffer = new GraphicBuffer(
+ grallocHandle, GraphicBuffer::CLONE_HANDLE,
+ width, height, format, 1, usage, stride);
+ native_handle_delete(grallocHandle);
+ AHardwareBuffer *hardwareBuffer =
+ AHardwareBuffer_from_GraphicBuffer(graphicBuffer.get());
+ AHardwareBuffer_acquire(hardwareBuffer);
+ apexBuffer->memory.graphic = hardwareBuffer;
+ return true;
+ }
+ return false;
+ }
+
+ ApexCodec_Component *mApexComponent;
+ std::weak_ptr<Listener> mListener;
+ std::weak_ptr<Component> mComponent;
+
+ std::thread mThread;
+ std::mutex mMutex;
+ std::condition_variable mCondition;
+ bool mStopped;
+ ApexCodec_BufferType mOutputBufferType;
+
+ size_t mLinearBlockCapacity;
+ uint32_t mWidth;
+ uint32_t mHeight;
+ uint32_t mFormat;
+
+ std::shared_ptr<C2BlockPool> mBlockPool;
+ std::list<std::unique_ptr<C2Work>> mWorkQueue;
+ std::map<uint64_t, std::unique_ptr<C2Work>> mWorkMap;
+};
+
// Codec2Client::Component::HidlBufferPoolSender
struct Codec2Client::Component::HidlBufferPoolSender :
hardware::media::c2::V1_1::utils::DefaultBufferPoolSender {
@@ -1168,6 +1944,13 @@
}
}
+Codec2Client::Codec2Client(ApexCodec_ComponentStore *base,
+ size_t serviceIndex)
+ : Configurable{nullptr, "android.componentStore.apexCodecs"},
+ mApexBase{base},
+ mServiceIndex{serviceIndex} {
+}
+
sp<Codec2Client::HidlBase> const& Codec2Client::getHidlBase() const {
return mHidlBase1_0;
}
@@ -1196,36 +1979,71 @@
const C2String& name,
const std::shared_ptr<Codec2Client::Listener>& listener,
std::shared_ptr<Codec2Client::Component>* const component) {
- if (mAidlBase) {
- std::shared_ptr<Component::AidlListener> aidlListener =
- Component::AidlListener::make<Component::AidlListener>();
- aidlListener->base = listener;
- std::shared_ptr<c2_aidl::IComponent> aidlComponent;
- ::ndk::ScopedAStatus transStatus = mAidlBase->createComponent(
- name,
- aidlListener,
- bufferpool2_aidl::implementation::ClientManager::getInstance(),
- &aidlComponent);
- c2_status_t status = GetC2Status(transStatus, "createComponent");
- if (status != C2_OK) {
- return status;
- } else if (!aidlComponent) {
- LOG(ERROR) << "createComponent(" << name.c_str()
- << ") -- null component.";
- return C2_CORRUPTED;
- }
- *component = std::make_shared<Codec2Client::Component>(aidlComponent);
- status = (*component)->setDeathListener((*component), listener);
- if (status != C2_OK) {
- LOG(ERROR) << "createComponent(" << name.c_str()
- << ") -- failed to set up death listener: "
- << status << ".";
- }
- (*component)->mAidlBufferPoolSender->setReceiver(mAidlHostPoolManager);
- aidlListener->component = *component;
- return status;
+ if (mApexBase) {
+ return createComponent_apex(name, listener, component);
+ } else if (mAidlBase) {
+ return createComponent_aidl(name, listener, component);
+ } else {
+ return createComponent_hidl(name, listener, component);
}
+}
+c2_status_t Codec2Client::createComponent_apex(
+ const C2String& name,
+ const std::shared_ptr<Codec2Client::Listener>& listener,
+ std::shared_ptr<Codec2Client::Component>* const component) {
+ if (__builtin_available(android 36, *)) {
+ ApexCodec_Component *apexComponent = nullptr;
+ ApexCodec_Status status = ApexCodec_Component_create(
+ mApexBase, name.c_str(), &apexComponent);
+ if (status != APEXCODEC_STATUS_OK) {
+ return (c2_status_t)status;
+ }
+ *component = std::make_shared<Codec2Client::Component>(apexComponent, name);
+ (*component)->initApexHandler(listener, *component);
+ return C2_OK;
+ } else {
+ return C2_OMITTED;
+ }
+}
+
+c2_status_t Codec2Client::createComponent_aidl(
+ const C2String& name,
+ const std::shared_ptr<Codec2Client::Listener>& listener,
+ std::shared_ptr<Codec2Client::Component>* const component) {
+ std::shared_ptr<Component::AidlListener> aidlListener =
+ Component::AidlListener::make<Component::AidlListener>();
+ aidlListener->base = listener;
+ std::shared_ptr<c2_aidl::IComponent> aidlComponent;
+ ::ndk::ScopedAStatus transStatus = mAidlBase->createComponent(
+ name,
+ aidlListener,
+ bufferpool2_aidl::implementation::ClientManager::getInstance(),
+ &aidlComponent);
+ c2_status_t status = GetC2Status(transStatus, "createComponent");
+ if (status != C2_OK) {
+ return status;
+ } else if (!aidlComponent) {
+ LOG(ERROR) << "createComponent(" << name.c_str()
+ << ") -- null component.";
+ return C2_CORRUPTED;
+ }
+ *component = std::make_shared<Codec2Client::Component>(aidlComponent);
+ status = (*component)->setDeathListener((*component), listener);
+ if (status != C2_OK) {
+ LOG(ERROR) << "createComponent(" << name.c_str()
+ << ") -- failed to set up death listener: "
+ << status << ".";
+ }
+ (*component)->mAidlBufferPoolSender->setReceiver(mAidlHostPoolManager);
+ aidlListener->component = *component;
+ return status;
+}
+
+c2_status_t Codec2Client::createComponent_hidl(
+ const C2String& name,
+ const std::shared_ptr<Codec2Client::Listener>& listener,
+ std::shared_ptr<Codec2Client::Component>* const component) {
c2_status_t status;
sp<Component::HidlListener> hidlListener = new Component::HidlListener{};
hidlListener->base = listener;
@@ -1593,6 +2411,13 @@
return a < b;
});
+ if (__builtin_available(android 36, *)) {
+ if (android::media::codec::provider_->in_process_sw_audio_codec_support()
+ && nullptr != ApexCodec_GetComponentStore()) {
+ names.push_back("__ApexCodecs__");
+ }
+ }
+
// Summarize to logcat.
if (names.empty()) {
LOG(INFO) << "No Codec2 services declared in the manifest.";
@@ -1649,7 +2474,13 @@
std::string const& name = GetServiceNames()[index];
LOG(VERBOSE) << "Creating a Codec2 client to service \"" << name << "\"";
- if (c2_aidl::utils::IsSelected()) {
+ if (name == "__ApexCodecs__") {
+ if (__builtin_available(android 36, *)) {
+ return std::make_shared<Codec2Client>(ApexCodec_GetComponentStore(), index);
+ } else {
+ LOG(FATAL) << "ApexCodecs not supported on Android version older than 36";
+ }
+ } else if (c2_aidl::utils::IsSelected()) {
if (__builtin_available(android __ANDROID_API_S__, *)) {
std::string instanceName =
::android::base::StringPrintf("%s/%s", AidlBase::descriptor, name.c_str());
@@ -2054,16 +2885,41 @@
mGraphicBufferAllocators{std::make_unique<GraphicBufferAllocators>()} {
}
+Codec2Client::Component::Component(ApexCodec_Component *base, const C2String &name)
+ : Configurable{[base]() -> ApexCodec_Configurable * {
+ if (__builtin_available(android 36, *)) {
+ return ApexCodec_Component_getConfigurable(base);
+ } else {
+ return nullptr;
+ }
+ }(), name},
+ mApexBase{base} {
+}
+
Codec2Client::Component::~Component() {
if (mAidlDeathSeq) {
GetAidlDeathManager()->unlinkToDeath(*mAidlDeathSeq, mAidlBase);
}
+ if (mApexBase) {
+ if (__builtin_available(android 36, *)) {
+ ApexCodec_Component_destroy(mApexBase);
+ }
+ mApexBase = nullptr;
+ }
}
c2_status_t Codec2Client::Component::createBlockPool(
C2Allocator::id_t id,
C2BlockPool::local_id_t* blockPoolId,
std::shared_ptr<Codec2Client::Configurable>* configurable) {
+ if (mApexBase) {
+ std::shared_ptr<C2BlockPool> blockPool;
+ CreateCodec2BlockPool(id, nullptr, &blockPool);
+ *blockPoolId = blockPool->getLocalId();
+ *configurable = nullptr;
+ mBlockPools[*blockPoolId] = blockPool;
+ return C2_OK;
+ }
if (mAidlBase) {
c2_aidl::IComponent::BlockPool aidlBlockPool;
c2_status_t status = C2_OK;
@@ -2134,6 +2990,10 @@
c2_status_t Codec2Client::Component::destroyBlockPool(
C2BlockPool::local_id_t localId) {
+ if (mApexBase) {
+ mBlockPools.erase(localId);
+ return C2_OK;
+ }
if (mAidlBase) {
mGraphicBufferAllocators->remove(localId);
::ndk::ScopedAStatus transStatus = mAidlBase->destroyBlockPool(localId);
@@ -2150,7 +3010,10 @@
void Codec2Client::Component::handleOnWorkDone(
const std::list<std::unique_ptr<C2Work>> &workItems) {
- if (mAidlBase) {
+ if (mApexBase) {
+ // no-op
+ return;
+ } else if (mAidlBase) {
holdIgbaBlocks(workItems);
} else {
// Output bufferqueue-based blocks' lifetime management
@@ -2160,6 +3023,10 @@
c2_status_t Codec2Client::Component::queue(
std::list<std::unique_ptr<C2Work>>* const items) {
+ if (mApexBase) {
+ mApexHandler->queue(*items);
+ return C2_OK;
+ }
if (mAidlBase) {
c2_aidl::WorkBundle workBundle;
if (!c2_aidl::utils::ToAidl(&workBundle, *items, mAidlBufferPoolSender.get())) {
@@ -2191,6 +3058,13 @@
C2Component::flush_mode_t mode,
std::list<std::unique_ptr<C2Work>>* const flushedWork) {
(void)mode; // Flush mode isn't supported in HIDL/AIDL yet.
+ if (mApexBase) {
+ if (__builtin_available(android 36, *)) {
+ return (c2_status_t)ApexCodec_Component_flush(mApexBase);
+ } else {
+ return C2_OMITTED;
+ }
+ }
c2_status_t status = C2_OK;
if (mAidlBase) {
c2_aidl::WorkBundle workBundle;
@@ -2250,6 +3124,9 @@
}
c2_status_t Codec2Client::Component::drain(C2Component::drain_mode_t mode) {
+ if (mApexBase) {
+ return C2_OMITTED;
+ }
if (mAidlBase) {
::ndk::ScopedAStatus transStatus = mAidlBase->drain(
mode == C2Component::DRAIN_COMPONENT_WITH_EOS);
@@ -2270,6 +3147,10 @@
}
c2_status_t Codec2Client::Component::start() {
+ if (mApexBase) {
+ // no-op
+ return C2_OK;
+ }
if (mAidlBase) {
::ndk::ScopedAStatus transStatus = mAidlBase->start();
return GetC2Status(transStatus, "start");
@@ -2306,6 +3187,13 @@
}
c2_status_t Codec2Client::Component::reset() {
+ if (mApexBase) {
+ if (__builtin_available(android 36, *)) {
+ return (c2_status_t)ApexCodec_Component_reset(mApexBase);
+ } else {
+ return C2_OMITTED;
+ }
+ }
if (mAidlBase) {
::ndk::ScopedAStatus transStatus = mAidlBase->reset();
return GetC2Status(transStatus, "reset");
@@ -2324,6 +3212,13 @@
}
c2_status_t Codec2Client::Component::release() {
+ if (mApexBase) {
+ if (__builtin_available(android 36, *)) {
+ return (c2_status_t)ApexCodec_Component_reset(mApexBase);
+ } else {
+ return C2_OMITTED;
+ }
+ }
if (mAidlBase) {
::ndk::ScopedAStatus transStatus = mAidlBase->release();
return GetC2Status(transStatus, "release");
@@ -2345,6 +3240,10 @@
uint32_t avSyncHwId,
native_handle_t** sidebandHandle) {
*sidebandHandle = nullptr;
+ if (mApexBase) {
+ // tunneling is not supported in APEX
+ return C2_OMITTED;
+ }
if (mAidlBase) {
::aidl::android::hardware::common::NativeHandle handle;
::ndk::ScopedAStatus transStatus = mAidlBase->configureVideoTunnel(avSyncHwId, &handle);
@@ -2616,6 +3515,10 @@
c2_status_t Codec2Client::Component::connectToInputSurface(
const std::shared_ptr<InputSurface>& inputSurface,
std::shared_ptr<InputSurfaceConnection>* connection) {
+ if (mApexBase) {
+ // FIXME
+ return C2_OMITTED;
+ }
if (mAidlBase) {
// FIXME
return C2_OMITTED;
@@ -2644,6 +3547,10 @@
const sp<HGraphicBufferProducer1>& producer,
const sp<HGraphicBufferSource>& source,
std::shared_ptr<InputSurfaceConnection>* connection) {
+ if (mApexBase) {
+ LOG(WARNING) << "Connecting to OMX input surface is not supported for AIDL C2 HAL";
+ return C2_OMITTED;
+ }
if (mAidlBase) {
LOG(WARNING) << "Connecting to OMX input surface is not supported for AIDL C2 HAL";
return C2_OMITTED;
@@ -2669,6 +3576,10 @@
}
c2_status_t Codec2Client::Component::disconnectFromInputSurface() {
+ if (mApexBase) {
+ // FIXME
+ return C2_OMITTED;
+ }
if (mAidlBase) {
// FIXME
return C2_OMITTED;
@@ -2693,6 +3604,16 @@
return sManager;
}
+c2_status_t Codec2Client::Component::initApexHandler(
+ const std::shared_ptr<Listener> &listener,
+ const std::shared_ptr<Component> &comp) {
+ if (!mApexBase) {
+ return C2_BAD_STATE;
+ }
+ mApexHandler = std::make_unique<ApexHandler>(mApexBase, listener, comp);
+ return C2_OK;
+}
+
c2_status_t Codec2Client::Component::setDeathListener(
const std::shared_ptr<Component>& component,
const std::shared_ptr<Listener>& listener) {
diff --git a/media/codec2/hal/client/include/codec2/hidl/client.h b/media/codec2/hal/client/include/codec2/hidl/client.h
index 7923f04..35c87e0 100644
--- a/media/codec2/hal/client/include/codec2/hidl/client.h
+++ b/media/codec2/hal/client/include/codec2/hidl/client.h
@@ -112,6 +112,10 @@
struct IGraphicBufferSource;
} // namespace android::hardware::media::omx::V1_0
+struct ApexCodec_ComponentStore;
+struct ApexCodec_Component;
+struct ApexCodec_Configurable;
+
namespace android {
// This class is supposed to be called Codec2Client::Configurable, but forward
@@ -148,6 +152,7 @@
explicit Codec2ConfigurableClient(const sp<HidlBase> &hidlBase);
explicit Codec2ConfigurableClient(const std::shared_ptr<AidlBase> &aidlBase);
+ Codec2ConfigurableClient(ApexCodec_Configurable *base, const C2String &name);
const C2String& getName() const;
@@ -172,6 +177,7 @@
private:
struct HidlImpl;
struct AidlImpl;
+ struct ApexImpl;
const std::unique_ptr<ImplBase> mImpl;
};
@@ -282,12 +288,16 @@
std::shared_ptr<AidlBase> const& base,
std::shared_ptr<Codec2ConfigurableClient::AidlBase> const& configurable,
size_t serviceIndex);
+ Codec2Client(
+ ApexCodec_ComponentStore* base,
+ size_t serviceIndex);
protected:
sp<HidlBase1_0> mHidlBase1_0;
sp<HidlBase1_1> mHidlBase1_1;
sp<HidlBase1_2> mHidlBase1_2;
std::shared_ptr<AidlBase> mAidlBase;
+ ApexCodec_ComponentStore* mApexBase{nullptr};
// Finds the first store where the predicate returns C2_OK and returns the
// last predicate result. The predicate will be tried on all stores. The
@@ -325,6 +335,20 @@
std::vector<C2Component::Traits> _listComponents(bool* success) const;
class Cache;
+
+private:
+ c2_status_t createComponent_aidl(
+ C2String const& name,
+ std::shared_ptr<Listener> const& listener,
+ std::shared_ptr<Component>* const component);
+ c2_status_t createComponent_hidl(
+ C2String const& name,
+ std::shared_ptr<Listener> const& listener,
+ std::shared_ptr<Component>* const component);
+ c2_status_t createComponent_apex(
+ C2String const& name,
+ std::shared_ptr<Listener> const& listener,
+ std::shared_ptr<Component>* const component);
};
struct Codec2Client::Interface : public Codec2Client::Configurable {
@@ -508,11 +532,16 @@
c2_status_t disconnectFromInputSurface();
+ c2_status_t initApexHandler(
+ const std::shared_ptr<Listener> &listener,
+ const std::shared_ptr<Component> &comp);
+
// base cannot be null.
Component(const sp<HidlBase>& base);
Component(const sp<HidlBase1_1>& base);
Component(const sp<HidlBase1_2>& base);
Component(const std::shared_ptr<AidlBase>& base);
+ Component(ApexCodec_Component* base, const C2String& name);
~Component();
@@ -521,12 +550,16 @@
sp<HidlBase1_1> mHidlBase1_1;
sp<HidlBase1_2> mHidlBase1_2;
std::shared_ptr<AidlBase> mAidlBase;
+ ApexCodec_Component *mApexBase{nullptr};
struct HidlBufferPoolSender;
struct AidlBufferPoolSender;
std::unique_ptr<HidlBufferPoolSender> mHidlBufferPoolSender;
std::unique_ptr<AidlBufferPoolSender> mAidlBufferPoolSender;
+ class ApexHandler;
+ std::unique_ptr<ApexHandler> mApexHandler;
+
struct OutputBufferQueue;
std::unique_ptr<OutputBufferQueue> mOutputBufferQueue;
@@ -547,6 +580,11 @@
const std::shared_ptr<Listener>& listener);
sp<::android::hardware::hidl_death_recipient> mDeathRecipient;
+ // This is a map of block pools created for APEX components in the client.
+ // Note that the APEX codec API requires output buffers to be passed from the client,
+ // so the client creates and keeps track of the block pools here.
+ std::map<C2BlockPool::local_id_t, std::shared_ptr<C2BlockPool>> mBlockPools;
+
friend struct Codec2Client;
struct HidlListener;
diff --git a/media/libaudioclient/AudioTrackShared.cpp b/media/libaudioclient/AudioTrackShared.cpp
index e3b79b2..359f3c1 100644
--- a/media/libaudioclient/AudioTrackShared.cpp
+++ b/media/libaudioclient/AudioTrackShared.cpp
@@ -310,8 +310,16 @@
ts = NULL;
break;
}
+
int32_t old = android_atomic_and(~CBLK_FUTEX_WAKE, &cblk->mFutex);
- if (!(old & CBLK_FUTEX_WAKE)) {
+
+ // Check inactive to prevent waiting if the track has been disabled due to underrun
+ // (or invalidated). The subsequent call to obtainBufer will return NOT_ENOUGH_DATA
+ // (or DEAD_OBJECT) and restart (or restore) the track.
+ const int32_t current_flags = android_atomic_acquire_load(&cblk->mFlags);
+ const bool inactive = current_flags & (CBLK_INVALID | CBLK_DISABLED);
+
+ if (!(old & CBLK_FUTEX_WAKE) && !inactive) {
if (measure && !beforeIsValid) {
clock_gettime(CLOCK_MONOTONIC, &before);
beforeIsValid = true;
diff --git a/media/libaudiohal/impl/EffectHalAidl.cpp b/media/libaudiohal/impl/EffectHalAidl.cpp
index 9fdde49..658fc18b 100644
--- a/media/libaudiohal/impl/EffectHalAidl.cpp
+++ b/media/libaudiohal/impl/EffectHalAidl.cpp
@@ -184,7 +184,7 @@
status_t EffectHalAidl::process() {
State state = State::INIT;
if (mConversion->isBypassing() || !mEffect->getState(&state).isOk() ||
- state != State::PROCESSING) {
+ (state != State::PROCESSING && state != State::DRAINING)) {
ALOGI("%s skipping process because it's %s", mEffectName.c_str(),
mConversion->isBypassing()
? "bypassing"
diff --git a/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp b/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp
index e8731ea..c11f908 100644
--- a/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp
+++ b/media/libaudiohal/tests/EffectHalVersionCompatibility_test.cpp
@@ -83,6 +83,7 @@
{Parameter::Id::visualizerTag, 1},
{Parameter::Id::volumeTag, 1},
{Parameter::Id::spatializerTag, 2},
+ {Parameter::Id::eraserTag, 3},
};
// Tags defined Parameter::Specific union.
static const std::unordered_map<Parameter::Specific::Tag, int /* version */>
@@ -104,6 +105,7 @@
{Parameter::Specific::visualizer, 1},
{Parameter::Specific::volume, 1},
{Parameter::Specific::spatializer, 2},
+ {Parameter::Specific::eraser, 3},
};
class MockFactory : public IFactory {
@@ -223,6 +225,7 @@
case Parameter::Id::virtualizerTag:
case Parameter::Id::visualizerTag:
case Parameter::Id::volumeTag:
+ case Parameter::Id::eraserTag:
FALLTHROUGH_INTENDED;
case Parameter::Id::spatializerTag: {
if (kParamIdEffectVersionMap.find(idTag) != kParamIdEffectVersionMap.end() &&
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
index d5e3cf7..5574ea1 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.cpp
@@ -90,6 +90,23 @@
}
}
+RetCode BundleContext::setCommon(const Parameter::Common& common) {
+ RetCode ret = EffectContext::setCommon(common);
+ RETURN_VALUE_IF(ret != RetCode::SUCCESS, ret, " setCommonFailed");
+ if (mInstance) {
+ LVM_ControlParams_t params;
+ RETURN_VALUE_IF(LVM_SUCCESS != LVM_GetControlParameters(mInstance, ¶ms),
+ RetCode::ERROR_EFFECT_LIB_ERROR, "failGetControlParams");
+ RETURN_VALUE_IF(RetCode::SUCCESS != applyCommonParameter(params),
+ RetCode::ERROR_EFFECT_LIB_ERROR, " applyCommonParameterFailed");
+ RETURN_VALUE_IF(LVM_SUCCESS != LVM_SetControlParameters(mInstance, ¶ms),
+ RetCode::ERROR_EFFECT_LIB_ERROR, "failSetControlParams");
+ } else {
+ RETURN_VALUE_IF(RetCode::SUCCESS != init(), RetCode::ERROR_EFFECT_LIB_ERROR, " initFailed");
+ }
+ return RetCode::SUCCESS;
+}
+
RetCode BundleContext::enable() {
if (mEnabled) return RetCode::ERROR_ILLEGAL_PARAMETER;
// Bass boost or Virtualizer can be temporarily disabled if playing over device speaker due to
@@ -599,7 +616,7 @@
return ret;
}
-RetCode BundleContext::initControlParameter(LVM_ControlParams_t& params) const {
+RetCode BundleContext::applyCommonParameter(LVM_ControlParams_t& params) const {
int outputChannelCount = ::aidl::android::hardware::audio::common::getChannelCount(
mCommon.output.base.channelMask);
auto outputChannelMaskConv = aidl2legacy_AudioChannelLayout_audio_channel_mask_t(
@@ -621,6 +638,13 @@
params.SourceFormat = LVM_MULTICHANNEL;
}
+ return RetCode::SUCCESS;
+}
+
+RetCode BundleContext::initControlParameter(LVM_ControlParams_t& params) const {
+ RETURN_VALUE_IF(RetCode::SUCCESS != applyCommonParameter(params),
+ RetCode::ERROR_EFFECT_LIB_ERROR, " applyCommonParameterFailed");
+
/* General parameters */
params.OperatingMode = LVM_MODE_ON;
params.SpeakerType = LVM_HEADPHONES;
diff --git a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
index e5ab40d..96f63cd 100644
--- a/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
+++ b/media/libeffects/lvm/wrapper/Aidl/BundleContext.h
@@ -35,6 +35,8 @@
void deInit();
lvm::BundleEffectType getBundleType() const { return mType; }
+ RetCode setCommon(const Parameter::Common& common) override;
+
RetCode enable() override;
RetCode enableOperatingMode();
RetCode disable() override;
@@ -133,6 +135,7 @@
bool isBandLevelIndexInRange(const std::vector<Equalizer::BandLevel>& bandLevels) const;
static LVM_EQNB_BandDef_t* getDefaultEqualizerBandDefs();
static LVM_HeadroomBandDef_t* getDefaultEqualizerHeadroomBanDefs();
+ RetCode applyCommonParameter(LVM_ControlParams_t& params) const;
};
} // namespace aidl::android::hardware::audio::effect
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index e434a3d..225cfdd 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -98,24 +98,6 @@
// static
const int64_t NuPlayer::Renderer::kMinPositionUpdateDelayUs = 100000ll;
-static audio_format_t constexpr audioFormatFromEncoding(int32_t pcmEncoding) {
- switch (pcmEncoding) {
- case kAudioEncodingPcmFloat:
- return AUDIO_FORMAT_PCM_FLOAT;
- case kAudioEncodingPcm32bit:
- return AUDIO_FORMAT_PCM_32_BIT;
- case kAudioEncodingPcm24bitPacked:
- return AUDIO_FORMAT_PCM_24_BIT_PACKED;
- case kAudioEncodingPcm16bit:
- return AUDIO_FORMAT_PCM_16_BIT;
- case kAudioEncodingPcm8bit:
- return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
- default:
- ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
- return AUDIO_FORMAT_INVALID;
- }
-}
-
NuPlayer::Renderer::Renderer(
const sp<MediaPlayerBase::AudioSink> &sink,
const sp<MediaClock> &mediaClock,
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
index 50eeb62..46606de 100644
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -2422,6 +2422,24 @@
return;
}
+audio_format_t audioFormatFromEncoding(int32_t pcmEncoding) {
+ switch (pcmEncoding) {
+ case kAudioEncodingPcmFloat:
+ return AUDIO_FORMAT_PCM_FLOAT;
+ case kAudioEncodingPcm32bit:
+ return AUDIO_FORMAT_PCM_32_BIT;
+ case kAudioEncodingPcm24bitPacked:
+ return AUDIO_FORMAT_PCM_24_BIT_PACKED;
+ case kAudioEncodingPcm16bit:
+ return AUDIO_FORMAT_PCM_16_BIT;
+ case kAudioEncodingPcm8bit:
+ return AUDIO_FORMAT_PCM_8_BIT; // TODO: do we want to support this?
+ default:
+ ALOGE("%s: Invalid encoding: %d", __func__, pcmEncoding);
+ return AUDIO_FORMAT_INVALID;
+ }
+}
+
status_t getAudioOffloadInfo(const sp<MetaData>& meta, bool hasVideo,
bool isStreaming, audio_stream_type_t streamType, audio_offload_info_t *info)
{
@@ -2441,6 +2459,12 @@
ALOGV("Mime type \"%s\" mapped to audio_format %d", mime, info->format);
}
+ int32_t pcmEncoding;
+ if (meta->findInt32(kKeyPcmEncoding, &pcmEncoding)) {
+ info->format = audioFormatFromEncoding(pcmEncoding);
+ ALOGV("audio_format use kKeyPcmEncoding value %d first", info->format);
+ }
+
if (AUDIO_FORMAT_INVALID == info->format) {
// can't offload if we don't know what the source format is
ALOGE("mime type \"%s\" not a known audio format", mime);
diff --git a/media/libstagefright/include/media/stagefright/Utils.h b/media/libstagefright/include/media/stagefright/Utils.h
index 1673120..e190374 100644
--- a/media/libstagefright/include/media/stagefright/Utils.h
+++ b/media/libstagefright/include/media/stagefright/Utils.h
@@ -44,6 +44,9 @@
// Convert a MIME type to a AudioSystem::audio_format
status_t mapMimeToAudioFormat(audio_format_t& format, const char* mime);
+// Convert a pcm-encoding to a AudioSystem::audio_format
+audio_format_t audioFormatFromEncoding(int32_t pcmEncoding);
+
// Convert a aac profile to a AudioSystem::audio_format
void mapAACProfileToAudioFormat(audio_format_t& format, uint64_t eAacProfile);
diff --git a/media/module/libapexcodecs/Android.bp b/media/module/libapexcodecs/Android.bp
index 790b749..dbda81b 100644
--- a/media/module/libapexcodecs/Android.bp
+++ b/media/module/libapexcodecs/Android.bp
@@ -40,6 +40,15 @@
}
+cc_library_headers {
+ name: "libapexcodecs-header",
+ visibility: [
+ "//frameworks/av/apex:__subpackages__",
+ "//frameworks/av/media/codec2/hal/client",
+ ],
+ export_include_dirs: ["include"],
+}
+
cc_library {
name: "libapexcodecs-testing",
defaults: ["libapexcodecs-defaults"],
diff --git a/media/ndk/NdkImageReader.cpp b/media/ndk/NdkImageReader.cpp
index 7b19ac0..7797841 100644
--- a/media/ndk/NdkImageReader.cpp
+++ b/media/ndk/NdkImageReader.cpp
@@ -655,6 +655,28 @@
}
}
+media_status_t
+AImageReader::setUsage(uint64_t usage) {
+ Mutex::Autolock _l(mLock);
+ if (!mIsOpen || mBufferItemConsumer == nullptr) {
+ ALOGE("not ready to perform setUsage()");
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ if (mUsage == usage) {
+ return AMEDIA_OK;
+ }
+
+ uint64_t halUsage = AHardwareBuffer_convertToGrallocUsageBits(mUsage);
+ status_t ret = mBufferItemConsumer->setConsumerUsageBits(halUsage);
+ if (ret != OK) {
+ ALOGE("setConsumerUsageBits() failed %d", ret);
+ return AMEDIA_ERROR_UNKNOWN;
+ }
+ mUsage = usage;
+ mHalUsage = halUsage;
+ return AMEDIA_OK;
+}
+
static
media_status_t validateParameters(int32_t width, int32_t height, int32_t format,
uint64_t usage, int32_t maxImages,
@@ -912,3 +934,14 @@
reader->setBufferRemovedListener(listener);
return AMEDIA_OK;
}
+
+EXPORT
+media_status_t AImageReader_setUsage(
+ AImageReader *reader, uint64_t usage) {
+ ALOGV("%s", __FUNCTION__);
+ if (reader == nullptr) {
+ ALOGE("%s: invalid argument! reader %p", __FUNCTION__, reader);
+ return AMEDIA_ERROR_INVALID_PARAMETER;
+ }
+ return reader->setUsage(usage);
+}
diff --git a/media/ndk/NdkImageReaderPriv.h b/media/ndk/NdkImageReaderPriv.h
index 0199616..1c50d83 100644
--- a/media/ndk/NdkImageReaderPriv.h
+++ b/media/ndk/NdkImageReaderPriv.h
@@ -20,6 +20,7 @@
#include <inttypes.h>
#include <media/NdkImageReader.h>
+#include <media-vndk/VndkImageReader.h>
#include <utils/List.h>
#include <utils/Mutex.h>
@@ -67,6 +68,7 @@
media_status_t setImageListener(AImageReader_ImageListener* listener);
media_status_t setBufferRemovedListener(AImageReader_BufferRemovedListener* listener);
+ media_status_t setUsage(uint64_t usage);
media_status_t acquireNextImage(/*out*/AImage** image, /*out*/int* fenceFd);
media_status_t acquireLatestImage(/*out*/AImage** image, /*out*/int* fenceFd);
@@ -120,7 +122,7 @@
const int32_t mWidth;
const int32_t mHeight;
int32_t mFormat;
- const uint64_t mUsage; // AHARDWAREBUFFER_USAGE_* flags.
+ uint64_t mUsage; // AHARDWAREBUFFER_USAGE_* flags.
const int32_t mMaxImages;
// TODO(jwcai) Seems completely unused in AImageReader class.
diff --git a/media/ndk/include/media-vndk/VndkImageReader.h b/media/ndk/include/media-vndk/VndkImageReader.h
new file mode 100644
index 0000000..c67a38c
--- /dev/null
+++ b/media/ndk/include/media-vndk/VndkImageReader.h
@@ -0,0 +1,51 @@
+/*
+ * 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.
+ */
+
+#ifndef _VNDK_IMAGE_READER_H
+#define _VNDK_IMAGE_READER_H
+
+// vndk is a superset of the NDK
+#include <media/NdkImageReader.h>
+
+__BEGIN_DECLS
+
+/**
+ * Set the usage of this image reader.
+ *
+ * <p>Note that calling this method will replace the previously set usage.</p>
+ *
+ * <p>Note: This will trigger re-allocation, could cause producer failures mid-stream
+ * if the new usage combination isn't supported, and thus should be avoided as much as
+ * possible regardless.</p>
+ *
+ * Available since API level 36.
+ *
+ * @param reader The image reader of interest.
+ * @param usage specifies how the consumer will access the AImage.
+ * See {@link AImageReader_newWithUsage} parameter description for more details.
+ * @return <ul>
+ * <li>{@link AMEDIA_OK} if the method call succeeds.</li>
+ * <li>{@link AMEDIA_ERROR_INVALID_PARAMETER} if reader is NULL.</li>
+ * <li>{@link AMEDIA_ERROR_UNKNOWN} if the method fails for some other reasons.</li></ul>
+ *
+ * @see AImage_getHardwareBuffer
+ */
+media_status_t AImageReader_setUsage(
+ AImageReader* _Nonnull reader, uint64_t usage) __INTRODUCED_IN(36);
+
+__END_DECLS
+
+#endif //_VNDK_IMAGE_READER_H
diff --git a/media/ndk/libmediandk.map.txt b/media/ndk/libmediandk.map.txt
index 35c696e..939f151 100644
--- a/media/ndk/libmediandk.map.txt
+++ b/media/ndk/libmediandk.map.txt
@@ -44,6 +44,7 @@
AImageReader_newWithDataSpace; # introduced=UpsideDownCake
AImageReader_setBufferRemovedListener; # introduced=26
AImageReader_setImageListener; # introduced=24
+ AImageReader_setUsage; # introduced=36 llndk
AImage_delete; # introduced=24
AImage_deleteAsync; # introduced=26
AImage_getCropRect; # introduced=24
diff --git a/media/utils/EventLogTags.logtags b/media/utils/EventLogTags.logtags
index c397f34..5b98b0f 100644
--- a/media/utils/EventLogTags.logtags
+++ b/media/utils/EventLogTags.logtags
@@ -31,7 +31,7 @@
# 6: Percent
# Default value for data of type int/long is 2 (bytes).
#
-# See system/core/logcat/event.logtags for the original definition of the tags.
+# See system/logging/logcat/event.logtags for the original definition of the tags.
# 61000 - 61199 reserved for audioserver
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index 8215247..282f3fa 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -98,10 +98,6 @@
static constexpr char kAudioServiceName[] = "audio";
-// In order to avoid invalidating offloaded tracks each time a Visualizer is turned on and off
-// we define a minimum time during which a global effect is considered enabled.
-static const nsecs_t kMinGlobalEffectEnabletimeNs = seconds(7200);
-
// Keep a strong reference to media.log service around forever.
// The service is within our parent process so it can never die in a way that we could observe.
// These two variables are const after initialization.
@@ -4842,11 +4838,6 @@
bool AudioFlinger::isNonOffloadableGlobalEffectEnabled_l() const
{
- if (mGlobalEffectEnableTime != 0 &&
- ((systemTime() - mGlobalEffectEnableTime) < kMinGlobalEffectEnabletimeNs)) {
- return true;
- }
-
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
const auto thread = mPlaybackThreads.valueAt(i);
audio_utils::lock_guard l(thread->mutex());
@@ -4862,8 +4853,6 @@
{
audio_utils::lock_guard _l(mutex());
- mGlobalEffectEnableTime = systemTime();
-
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
const sp<IAfPlaybackThread> t = mPlaybackThreads.valueAt(i);
if (t->type() == IAfThreadBase::OFFLOAD) {
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 6777075..c229e83 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -760,9 +760,6 @@
std::atomic<size_t> mClientSharedHeapSize = kMinimumClientSharedHeapSizeBytes;
static constexpr size_t kMinimumClientSharedHeapSizeBytes = 1024 * 1024; // 1MB
- // when a global effect was last enabled
- nsecs_t mGlobalEffectEnableTime GUARDED_BY(mutex()) = 0;
-
/* const */ sp<IAfPatchPanel> mPatchPanel;
const sp<EffectsFactoryHalInterface> mEffectsFactoryHal =
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 776775b..1cb9ea4 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -7760,6 +7760,9 @@
audio_utils::lock_guard l(mutex());
localTracks = std::move(mOutputTracks);
mOutputTracks.clear();
+ for (size_t i = 0; i < localTracks.size(); ++i) {
+ localTracks[i]->destroy();
+ }
}
localTracks.clear();
outputTracks.clear();
@@ -8418,7 +8421,6 @@
}
if (invalidate) {
activeTrack->invalidate();
- ALOG_ASSERT(fastTrackToRemove == 0);
fastTrackToRemove = activeTrack;
removeTrack_l(activeTrack);
mActiveTracks.remove(activeTrack);
diff --git a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
index f7b9b33..9107e2a 100644
--- a/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
+++ b/services/audiopolicy/common/managerdefinitions/include/EffectDescriptor.h
@@ -75,7 +75,8 @@
bool isEffectEnabled(int id) const;
uint32_t getMaxEffectsCpuLoad() const;
uint32_t getMaxEffectsMemory() const;
- bool isNonOffloadableEffectEnabled() const;
+ bool isNonOffloadableEffectEnabled(
+ const std::optional<const effect_uuid_t>& uuid = std::nullopt) const;
void moveEffects(audio_session_t session,
audio_io_handle_t srcOutput,
diff --git a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
index 090da6c..6d66781 100644
--- a/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
+++ b/services/audiopolicy/common/managerdefinitions/src/EffectDescriptor.cpp
@@ -21,6 +21,7 @@
#include "AudioInputDescriptor.h"
#include "EffectDescriptor.h"
+#include <system/audio_effects/audio_effects_utils.h>
#include <utils/String8.h>
#include <AudioPolicyInterface.h>
@@ -157,14 +158,18 @@
return NO_ERROR;
}
-bool EffectDescriptorCollection::isNonOffloadableEffectEnabled() const
+bool EffectDescriptorCollection::isNonOffloadableEffectEnabled(
+ const std::optional<const effect_uuid_t>& uuid) const
{
+ using namespace android::effect::utils;
for (size_t i = 0; i < size(); i++) {
sp<EffectDescriptor> effectDesc = valueAt(i);
- if (effectDesc->mEnabled && (effectDesc->isMusicEffect()) &&
- ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) {
- ALOGV("isNonOffloadableEffectEnabled() non offloadable effect %s enabled on session %d",
- effectDesc->mDesc.name, effectDesc->mSession);
+ if ((effectDesc->mEnabled && (effectDesc->isMusicEffect()) &&
+ ((effectDesc->mDesc.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) == 0)) &&
+ (uuid == std::nullopt || uuid.value() == effectDesc->mDesc.uuid)) {
+ ALOGE("%s: non offloadable effect %s, uuid %s, enabled on session %d", __func__,
+ effectDesc->mDesc.name, ToString(effectDesc->mDesc.uuid).c_str(),
+ effectDesc->mSession);
return true;
}
}
diff --git a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
index 354c59c..74e77e8 100644
--- a/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
+++ b/services/audiopolicy/managerdefault/AudioPolicyManager.cpp
@@ -1492,7 +1492,8 @@
for (auto &secondaryMix : secondaryMixes) {
sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
if (outputDesc != nullptr &&
- outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE) {
+ outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE &&
+ outputDesc->mIoHandle != *output) {
secondaryOutputs->push_back(outputDesc->mIoHandle);
weakSecondaryOutputDescs.push_back(outputDesc);
}
@@ -7432,7 +7433,8 @@
for (auto &secondaryMix : secondaryMixes) {
sp<SwAudioOutputDescriptor> outputDesc = secondaryMix->getOutput();
if (outputDesc != nullptr &&
- outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE) {
+ outputDesc->mIoHandle != AUDIO_IO_HANDLE_NONE &&
+ outputDesc != outputDescriptor) {
secondaryDescs.push_back(outputDesc);
}
}
diff --git a/services/audiopolicy/tests/Android.bp b/services/audiopolicy/tests/Android.bp
index c600fb6..154b063 100644
--- a/services/audiopolicy/tests/Android.bp
+++ b/services/audiopolicy/tests/Android.bp
@@ -53,7 +53,10 @@
"libaudiopolicymanager_interface_headers",
],
- srcs: ["audiopolicymanager_tests.cpp"],
+ srcs: [
+ "audiopolicymanager_tests.cpp",
+ "test_execution_tracer.cpp",
+ ],
data: [":audiopolicytest_configuration_files"],
diff --git a/services/audiopolicy/tests/audiopolicymanager_tests.cpp b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
index 5278b73..5402bfe 100644
--- a/services/audiopolicy/tests/audiopolicymanager_tests.cpp
+++ b/services/audiopolicy/tests/audiopolicymanager_tests.cpp
@@ -44,6 +44,7 @@
#include "AudioPolicyManagerTestClient.h"
#include "AudioPolicyTestClient.h"
#include "AudioPolicyTestManager.h"
+#include "test_execution_tracer.h"
using namespace android;
using testing::UnorderedElementsAre;
@@ -4145,3 +4146,9 @@
testing::Values(AUDIO_USAGE_NOTIFICATION_TELEPHONY_RINGTONE,
AUDIO_USAGE_ALARM)
);
+
+int main(int argc, char** argv) {
+ ::testing::InitGoogleTest(&argc, argv);
+ ::testing::UnitTest::GetInstance()->listeners().Append(new TestExecutionTracer());
+ return RUN_ALL_TESTS();
+}
diff --git a/services/audiopolicy/tests/test_execution_tracer.cpp b/services/audiopolicy/tests/test_execution_tracer.cpp
new file mode 100644
index 0000000..09de4a1
--- /dev/null
+++ b/services/audiopolicy/tests/test_execution_tracer.cpp
@@ -0,0 +1,43 @@
+/*
+ * 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.
+ */
+
+#define LOG_TAG "TestExecutionTracer"
+
+#include "test_execution_tracer.h"
+
+#include <android-base/logging.h>
+
+void TestExecutionTracer::OnTestStart(const ::testing::TestInfo& test_info) {
+ TraceTestState("Started", test_info);
+}
+
+void TestExecutionTracer::OnTestEnd(const ::testing::TestInfo& test_info) {
+ TraceTestState("Finished", test_info);
+}
+
+void TestExecutionTracer::OnTestPartResult(const ::testing::TestPartResult& result) {
+ if (result.failed()) {
+ LOG(ERROR) << result;
+ } else {
+ LOG(INFO) << result;
+ }
+}
+
+// static
+void TestExecutionTracer::TraceTestState(const std::string& state,
+ const ::testing::TestInfo& test_info) {
+ LOG(INFO) << state << " " << test_info.test_suite_name() << "::" << test_info.name();
+}
diff --git a/services/audiopolicy/tests/test_execution_tracer.h b/services/audiopolicy/tests/test_execution_tracer.h
new file mode 100644
index 0000000..9031aaf
--- /dev/null
+++ b/services/audiopolicy/tests/test_execution_tracer.h
@@ -0,0 +1,29 @@
+/*
+ * 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.
+ */
+
+#pragma once
+
+#include <gtest/gtest.h>
+
+class TestExecutionTracer : public ::testing::EmptyTestEventListener {
+ public:
+ void OnTestStart(const ::testing::TestInfo& test_info) override;
+ void OnTestEnd(const ::testing::TestInfo& test_info) override;
+ void OnTestPartResult(const ::testing::TestPartResult& result) override;
+
+ private:
+ static void TraceTestState(const std::string& state, const ::testing::TestInfo& test_info);
+};
diff --git a/services/camera/virtualcamera/VirtualCameraSession.cc b/services/camera/virtualcamera/VirtualCameraSession.cc
index e1815c7..d074826 100644
--- a/services/camera/virtualcamera/VirtualCameraSession.cc
+++ b/services/camera/virtualcamera/VirtualCameraSession.cc
@@ -191,7 +191,11 @@
}
halStream.overrideDataSpace = stream.dataSpace;
- halStream.producerUsage = BufferUsage::GPU_RENDER_TARGET;
+ halStream.producerUsage = static_cast<BufferUsage>(
+ static_cast<int64_t>(stream.usage) |
+ static_cast<int64_t>(BufferUsage::CAMERA_OUTPUT) |
+ static_cast<int64_t>(BufferUsage::GPU_RENDER_TARGET));
+
halStream.supportOffline = false;
return halStream;
}