/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "CCodecConfig.h" #include #include #include #include #include #include #include namespace { enum ExtendedC2ParamIndexKind : C2Param::type_index_t { kParamIndexVendorInt32 = C2Param::TYPE_INDEX_VENDOR_START, kParamIndexVendorInt64, kParamIndexVendorString, }; typedef C2PortParam C2PortVendorInt32Info; constexpr char C2_PARAMKEY_VENDOR_INT32[] = "example.int32"; constexpr char KEY_VENDOR_INT32[] = "vendor.example.int32.value"; typedef C2StreamParam C2StreamVendorInt64Info; constexpr char C2_PARAMKEY_VENDOR_INT64[] = "example.int64"; constexpr char KEY_VENDOR_INT64[] = "vendor.example.int64.value"; typedef C2PortParam C2PortVendorStringInfo; constexpr char C2_PARAMKEY_VENDOR_STRING[] = "example.string"; constexpr char KEY_VENDOR_STRING[] = "vendor.example.string.value"; } // namespace namespace android { class CCodecConfigTest : public ::testing::Test { public: constexpr static int32_t kCodec2Int32 = 0xC0DEC2; constexpr static int64_t kCodec2Int64 = 0xC0DEC2C0DEC2ll; constexpr static char kCodec2Str[] = "codec2"; CCodecConfigTest() : mReflector{std::make_shared()} { initializeSystemResources(); } void init( C2Component::domain_t domain, C2Component::kind_t kind, const char *mediaType) { sp cachedConfigurable = new hardware::media::c2::V1_0::utils::CachedConfigurable( std::make_unique(mReflector, domain, kind, mediaType, mSystemResources, mExcludedResources)); cachedConfigurable->init(std::make_shared()); mConfigurable = std::make_shared(cachedConfigurable); } struct Cache : public hardware::media::c2::V1_0::utils::ParameterCache { c2_status_t validate(const std::vector>&) override { return C2_OK; } }; class Configurable : public hardware::media::c2::V1_0::utils::ConfigurableC2Intf { public: Configurable( const std::shared_ptr &reflector, C2Component::domain_t domain, C2Component::kind_t kind, const char *mediaType, const std::vector& systemResources, const std::vector& excludedResources) : ConfigurableC2Intf("name", 0u), mImpl(reflector, domain, kind, mediaType, systemResources, excludedResources) { } c2_status_t query( const std::vector &indices, c2_blocking_t mayBlock, std::vector>* const params) const override { return mImpl.query({}, indices, mayBlock, params); } c2_status_t config( const std::vector ¶ms, c2_blocking_t mayBlock, std::vector>* const failures) override { return mImpl.config(params, mayBlock, failures); } c2_status_t querySupportedParams( std::vector>* const params) const override { return mImpl.querySupportedParams(params); } c2_status_t querySupportedValues( std::vector& fields, c2_blocking_t mayBlock) const override { return mImpl.querySupportedValues(fields, mayBlock); } private: class Impl : public C2InterfaceHelper { public: Impl(const std::shared_ptr &reflector, C2Component::domain_t domain, C2Component::kind_t kind, const char *mediaType, const std::vector& systemResources, const std::vector& excludedResources) : C2InterfaceHelper{reflector} { setDerivedInstance(this); addParameter( DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN) .withConstValue(new C2ComponentDomainSetting(domain)) .build()); addParameter( DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND) .withConstValue(new C2ComponentKindSetting(kind)) .build()); addParameter( DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT) .withConstValue(new C2PortStreamCountTuning::input(1)) .build()); addParameter( DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT) .withConstValue(new C2PortStreamCountTuning::output(1)) .build()); const char *rawMediaType = ""; switch (domain) { case C2Component::DOMAIN_IMAGE: [[fallthrough]]; case C2Component::DOMAIN_VIDEO: rawMediaType = MIMETYPE_VIDEO_RAW; break; case C2Component::DOMAIN_AUDIO: rawMediaType = MIMETYPE_AUDIO_RAW; break; default: break; } bool isEncoder = kind == C2Component::KIND_ENCODER; std::string inputMediaType{isEncoder ? rawMediaType : mediaType}; std::string outputMediaType{isEncoder ? mediaType : rawMediaType}; auto allocSharedString = [](const auto ¶m, const std::string &str) { typedef typename std::remove_reference::type::element_type T; std::shared_ptr ret = T::AllocShared(str.length() + 1); strcpy(ret->m.value, str.c_str()); return ret; }; addParameter( DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE) .withConstValue(allocSharedString(mInputMediaType, inputMediaType)) .build()); addParameter( DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE) .withConstValue(allocSharedString(mOutputMediaType, outputMediaType)) .build()); addParameter( DefineParam(mInt32Input, C2_PARAMKEY_VENDOR_INT32) .withDefault(new C2PortVendorInt32Info::input(0)) .withFields({C2F(mInt32Input, value).any()}) .withSetter(Setter) .build()); addParameter( DefineParam(mInt64Output, C2_PARAMKEY_VENDOR_INT64) .withDefault(new C2StreamVendorInt64Info::output(0u, 0)) .withFields({C2F(mInt64Output, value).any()}) .withSetter(Setter) .build()); addParameter( DefineParam(mStringInput, C2_PARAMKEY_VENDOR_STRING) .withDefault(decltype(mStringInput)::element_type::AllocShared(1, "")) .withFields({C2F(mStringInput, m.value).any()}) .withSetter(Setter) .build()); addParameter( DefineParam(mPixelAspectRatio, C2_PARAMKEY_PIXEL_ASPECT_RATIO) .withDefault(new C2StreamPixelAspectRatioInfo::output(0u, 1, 1)) .withFields({ C2F(mPixelAspectRatio, width).any(), C2F(mPixelAspectRatio, height).any(), }) .withSetter(Setter) .build()); // Add System Resource Capacity addParameter( DefineParam(mResourcesCapacity, C2_PARAMKEY_RESOURCES_CAPACITY) .withDefault(C2ResourcesCapacityTuning::AllocShared( systemResources.size(), systemResources)) .withFields({ C2F(mResourcesCapacity, m.values[0].id).any(), C2F(mResourcesCapacity, m.values[0].kind).any(), C2F(mResourcesCapacity, m.values[0].amount).any(), }) .withSetter(Setter) .build()); // Add Excluded System Resources addParameter( DefineParam(mResourcesExcluded, C2_PARAMKEY_RESOURCES_EXCLUDED) .withDefault(C2ResourcesExcludedTuning::AllocShared( excludedResources.size(), excludedResources)) .withFields({ C2F(mResourcesExcluded, m.values[0].id).any(), C2F(mResourcesExcluded, m.values[0].kind).any(), C2F(mResourcesExcluded, m.values[0].amount).any(), }) .withSetter(Setter) .build()); if (isEncoder) { addParameter( DefineParam(mInputBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::input(0u)) .withFields({C2F(mInputBitrate, value).any()}) .withSetter(Setter) .build()); addParameter( DefineParam(mOutputBitrate, C2_PARAMKEY_BITRATE) .withDefault(new C2StreamBitrateInfo::output(0u)) .withFields({C2F(mOutputBitrate, value).any()}) .calculatedAs( Copy, mInputBitrate) .build()); addParameter( DefineParam(mOutputProfileLevel, C2_PARAMKEY_PROFILE_LEVEL) .withDefault(new C2StreamProfileLevelInfo::output( 0u, PROFILE_UNUSED, LEVEL_UNUSED)) .withFields({ C2F(mOutputProfileLevel, profile).any(), C2F(mOutputProfileLevel, level).any(), }) .withSetter(Setter) .build()); std::vector c2QpOffsetRectsInfo; addParameter( DefineParam(mInputQpOffsetRects, C2_PARAMKEY_QP_OFFSET_RECTS) .withDefault(C2StreamQpOffsetRects::output::AllocShared( c2QpOffsetRectsInfo.size(), 0, c2QpOffsetRectsInfo)) .withFields({ C2F(mInputQpOffsetRects, m.values[0].qpOffset) .inRange(-128, 127), C2F(mInputQpOffsetRects, m.values[0].left).any(), C2F(mInputQpOffsetRects, m.values[0].top).any(), C2F(mInputQpOffsetRects, m.values[0].width).any(), C2F(mInputQpOffsetRects, m.values[0].height).any(), }) .withSetter(Setter) .build()); } // TODO: more SDK params } private: std::shared_ptr mDomain; std::shared_ptr mKind; std::shared_ptr mInputStreamCount; std::shared_ptr mOutputStreamCount; std::shared_ptr mInputMediaType; std::shared_ptr mOutputMediaType; std::shared_ptr mInt32Input; std::shared_ptr mInt64Output; std::shared_ptr mStringInput; std::shared_ptr mPixelAspectRatio; std::shared_ptr mInputBitrate; std::shared_ptr mOutputBitrate; std::shared_ptr mInputProfileLevel; std::shared_ptr mOutputProfileLevel; std::shared_ptr mInputQpOffsetRects; std::shared_ptr mResourcesCapacity; std::shared_ptr mResourcesExcluded; template static C2R Setter(bool, C2P &) { return C2R::Ok(); } template static C2R Copy(bool, C2P &me, const C2P &dep) { me.set().value = dep.v.value; return C2R::Ok(); } }; Impl mImpl; }; std::shared_ptr mReflector; std::shared_ptr mConfigurable; CCodecConfig mConfig; /* * This test tracks two system resources: * - max instance limit, which is capped at 64 * - max pixel count: up to 4 instances of 4K ==> 4 * 3840 * 2400 * * These 2 resource types are given 2 different ids as below. */ void initializeSystemResources() { // max instance limit 64 const uint32_t kMaxInstanceCount = 0x1000; // max pixel count: up to 4 instances of 4K const uint32_t kMaxPixelCount = 0x1001; mSystemResources.push_back(C2SystemResourceStruct(kMaxInstanceCount, CONST, 64)); mSystemResources.push_back(C2SystemResourceStruct(kMaxPixelCount, CONST, 4 * 3840 * 2400)); // Nothing is excluded, but lets just add them with amount as 0. mExcludedResources.push_back(C2SystemResourceStruct(kMaxInstanceCount, CONST, 0)); mExcludedResources.push_back(C2SystemResourceStruct(kMaxPixelCount, CONST, 0)); } bool validateSystemResources(const std::vector& resources) const { if (resources.size() != mSystemResources.size()) { return false; } for (const auto& resource : mSystemResources) { auto found = std::find_if(resources.begin(), resources.end(), [resource](const C2SystemResourceStruct& item) { return (item.id == resource.id && item.kind == resource.kind && item.amount == resource.amount); }); if (found == resources.end()) { return false; } } return true; } private: std::vector mSystemResources; std::vector mExcludedResources; }; using D = CCodecConfig::Domain; template T *FindParam(const std::vector> &vec) { for (const std::unique_ptr ¶m : vec) { if (param->coreIndex() == T::CORE_INDEX) { return static_cast(param.get()); } } return nullptr; } TEST_F(CCodecConfigTest, SetVendorParam) { // Test at audio domain, as video domain has a few local parameters that // interfere with the testing. init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); sp format{new AMessage}; format->setInt32(KEY_VENDOR_INT32, kCodec2Int32); format->setInt64(KEY_VENDOR_INT64, kCodec2Int64); format->setString(KEY_VENDOR_STRING, kCodec2Str); std::vector> configUpdate; ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams( mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate)); ASSERT_EQ(3u, configUpdate.size()); C2PortVendorInt32Info::input *i32 = FindParam::type>(configUpdate); ASSERT_NE(nullptr, i32); ASSERT_EQ(kCodec2Int32, i32->value); C2StreamVendorInt64Info::output *i64 = FindParam::type>(configUpdate); ASSERT_NE(nullptr, i64); ASSERT_EQ(kCodec2Int64, i64->value); C2PortVendorStringInfo::input *str = FindParam::type>(configUpdate); ASSERT_NE(nullptr, str); ASSERT_STREQ(kCodec2Str, str->m.value); } TEST_F(CCodecConfigTest, VendorParamUpdate_Unsubscribed) { // Test at audio domain, as video domain has a few local parameters that // interfere with the testing. init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); std::vector> configUpdate; C2PortVendorInt32Info::input i32(kCodec2Int32); C2StreamVendorInt64Info::output i64(0u, kCodec2Int64); std::unique_ptr str = C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str); configUpdate.push_back(C2Param::Copy(i32)); configUpdate.push_back(C2Param::Copy(i64)); configUpdate.push_back(std::move(str)); // The vendor parameters are not yet subscribed ASSERT_FALSE(mConfig.updateConfiguration(configUpdate, D::ALL)); int32_t vendorInt32{0}; ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); int64_t vendorInt64{0}; ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); AString vendorString; ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); } TEST_F(CCodecConfigTest, VendorParamUpdate_AllSubscribed) { // Test at audio domain, as video domain has a few local parameters that // interfere with the testing. init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); // Force subscribe to all vendor params ASSERT_EQ(OK, mConfig.subscribeToAllVendorParams(mConfigurable, C2_MAY_BLOCK)); std::vector> configUpdate; C2PortVendorInt32Info::input i32(kCodec2Int32); C2StreamVendorInt64Info::output i64(0u, kCodec2Int64); std::unique_ptr str = C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str); configUpdate.push_back(C2Param::Copy(i32)); configUpdate.push_back(C2Param::Copy(i64)); configUpdate.push_back(std::move(str)); ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL)); int32_t vendorInt32{0}; ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_EQ(kCodec2Int32, vendorInt32); ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); int64_t vendorInt64{0}; ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_TRUE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_EQ(kCodec2Int64, vendorInt64); AString vendorString; ASSERT_TRUE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_STREQ(kCodec2Str, vendorString.c_str()); ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); } TEST_F(CCodecConfigTest, VendorParamUpdate_PartiallySubscribed) { // Test at audio domain, as video domain has a few local parameters that // interfere with the testing. init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); // Subscribe to example.int32 only std::vector> configUpdate; sp format{new AMessage}; format->setInt32(KEY_VENDOR_INT32, 0); configUpdate.clear(); ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams( mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate)); ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK)); C2PortVendorInt32Info::input i32(kCodec2Int32); C2StreamVendorInt64Info::output i64(0u, kCodec2Int64); std::unique_ptr str = C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str); configUpdate.clear(); configUpdate.push_back(C2Param::Copy(i32)); configUpdate.push_back(C2Param::Copy(i64)); configUpdate.push_back(std::move(str)); // Only example.i32 should be updated ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL)); int32_t vendorInt32{0}; ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_EQ(kCodec2Int32, vendorInt32); ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); int64_t vendorInt64{0}; ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); AString vendorString; ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); } TEST_F(CCodecConfigTest, SetPixelAspectRatio) { init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); sp format{new AMessage}; format->setInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, 12); format->setInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, 11); std::vector> configUpdate; ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams( mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate)); ASSERT_EQ(1u, configUpdate.size()); C2StreamPixelAspectRatioInfo::output *par = FindParam::type>(configUpdate); ASSERT_NE(nullptr, par); ASSERT_EQ(12, par->width); ASSERT_EQ(11, par->height); } TEST_F(CCodecConfigTest, PixelAspectRatioUpdate) { init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); std::vector> configUpdate; C2StreamPixelAspectRatioInfo::output par(0u, 12, 11); configUpdate.push_back(C2Param::Copy(par)); ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL)); int32_t parWidth{0}; ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, &parWidth)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_EQ(12, parWidth); ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, &parWidth)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); int32_t parHeight{0}; ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, &parHeight)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_EQ(11, parHeight); ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, &parHeight)) << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str(); } TEST_F(CCodecConfigTest, DataspaceUpdate) { init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, MIMETYPE_VIDEO_AVC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); class InputSurfaceStub : public InputSurfaceWrapper { public: ~InputSurfaceStub() override = default; status_t connect(const std::shared_ptr &) override { return OK; } void disconnect() override {} status_t start() override { return OK; } status_t signalEndOfInputStream() override { return OK; } status_t configure(Config &) override { return OK; } }; mConfig.mInputSurface = std::make_shared(); sp format{new AMessage}; format->setInt32(KEY_COLOR_RANGE, COLOR_RANGE_LIMITED); format->setInt32(KEY_COLOR_STANDARD, COLOR_STANDARD_BT709); format->setInt32(KEY_COLOR_TRANSFER, COLOR_TRANSFER_SDR_VIDEO); format->setInt32(KEY_BIT_RATE, 100); std::vector> configUpdate; ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams( mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate)); ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL)); int32_t range{0}; ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_RANGE_LIMITED, range) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); int32_t standard{0}; ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_STANDARD_BT709, standard) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); int32_t transfer{0}; ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_TRANSFER_SDR_VIDEO, transfer) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); mConfig.mInputSurface->setDataSpace(HAL_DATASPACE_BT2020_PQ); // Dataspace from input surface should override the configured setting mConfig.updateFormats(D::ALL); ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_RANGE_FULL, range) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_STANDARD_BT2020, standard) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_TRANSFER_ST2084, transfer) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); // Simulate bitrate update format = new AMessage; format->setInt32(KEY_BIT_RATE, 200); configUpdate.clear(); ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams( mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate)); ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK)); // Color information should remain the same mConfig.updateFormats(D::ALL); ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_RANGE_FULL, range) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_STANDARD_BT2020, standard) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer)) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); EXPECT_EQ(COLOR_TRANSFER_ST2084, transfer) << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str(); } typedef std::tuple HdrProfilesParams; class HdrProfilesTest : public CCodecConfigTest, public ::testing::WithParamInterface { }; TEST_P(HdrProfilesTest, SetFromSdk) { HdrProfilesParams params = GetParam(); std::string mediaType = std::get<0>(params); C2Config::profile_t c2Profile = std::get<1>(params); int32_t sdkProfile = std::get<2>(params); init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, mediaType.c_str()); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); sp format{new AMessage}; format->setInt32(KEY_PROFILE, sdkProfile); std::vector> configUpdate; ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams( mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate)); ASSERT_EQ(1u, configUpdate.size()); C2StreamProfileLevelInfo::input *pl = FindParam::type>(configUpdate); ASSERT_NE(nullptr, pl); ASSERT_EQ(c2Profile, pl->profile); } HdrProfilesParams kHdrProfilesParams[] = { std::make_tuple(MIMETYPE_VIDEO_HEVC, PROFILE_HEVC_MAIN_10, HEVCProfileMain10HDR10), std::make_tuple(MIMETYPE_VIDEO_HEVC, PROFILE_HEVC_MAIN_10, HEVCProfileMain10HDR10Plus), std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_2, VP9Profile2HDR), std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_2, VP9Profile2HDR10Plus), std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_3, VP9Profile3HDR), std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_3, VP9Profile3HDR10Plus), std::make_tuple(MIMETYPE_VIDEO_AV1, PROFILE_AV1_0, AV1ProfileMain10HDR10), std::make_tuple(MIMETYPE_VIDEO_AV1, PROFILE_AV1_0, AV1ProfileMain10HDR10Plus), }; INSTANTIATE_TEST_SUITE_P( CCodecConfig, HdrProfilesTest, ::testing::ValuesIn(kHdrProfilesParams)); TEST_F(CCodecConfigTest, SetRegionOfInterestParams) { if (!android::media::codec::provider_->region_of_interest() || !android::media::codec::provider_->region_of_interest_support()) { GTEST_SKIP() << "Skipping the test as region_of_interest flags are not enabled.\n"; } init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, MIMETYPE_VIDEO_VP9); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); const int kWidth = 32; const int kHeight = 32; const int kNumBlocks = ((kWidth + 15) / 16) * ((kHeight + 15) / 16); int8_t mapInfo[kNumBlocks] = {-1, 0, 1, 1}; int top[kNumBlocks] = {0, 0, 16, 16}; int left[kNumBlocks] = {0, 16, 0, 16}; int bottom[kNumBlocks] = {16, 16, 32, 32}; int right[kNumBlocks] = {16, 32, 16, 32}; sp format{new AMessage}; format->setInt32(KEY_WIDTH, kWidth); format->setInt32(KEY_HEIGHT, kHeight); AString val; for (int i = 0; i < kNumBlocks; i++) { val.append(AStringPrintf("%d,%d-%d,%d=%d;", top[i], left[i], bottom[i], right[i], mapInfo[i])); } format->setString(PARAMETER_KEY_QP_OFFSET_RECTS, val); std::vector> configUpdate; ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(mConfigurable, format, D::CONFIG, C2_MAY_BLOCK, &configUpdate)); EXPECT_EQ(1u, configUpdate.size()); C2StreamQpOffsetRects::output* qpRectParam = FindParam::type>(configUpdate); ASSERT_NE(nullptr, qpRectParam); ASSERT_EQ(kNumBlocks, qpRectParam->flexCount()); for (auto i = 0; i < kNumBlocks; i++) { EXPECT_EQ(mapInfo[i], (int8_t)qpRectParam->m.values[i].qpOffset) << "qp offset for index " << i << " is not as expected "; EXPECT_EQ(left[i], qpRectParam->m.values[i].left) << "left for index " << i << " is not as expected "; EXPECT_EQ(top[i], qpRectParam->m.values[i].top) << "top for index " << i << " is not as expected "; EXPECT_EQ(right[i] - left[i], qpRectParam->m.values[i].width) << "width for index " << i << " is not as expected "; EXPECT_EQ(bottom[i] - top[i], qpRectParam->m.values[i].height) << "height for index " << i << " is not as expected "; } } static c2_status_t queryGlobalResources(std::shared_ptr& configurable, std::vector& resources) { std::vector> heapParams; c2_status_t c2err = configurable->query( {}, {C2ResourcesCapacityTuning::PARAM_TYPE, C2ResourcesExcludedTuning::PARAM_TYPE}, C2_MAY_BLOCK, &heapParams); if (c2err == C2_OK && heapParams.size() == 2u) { // Construct Globally available resources now. // Get the total capacity first. const C2ResourcesCapacityTuning* systemCapacity = C2ResourcesCapacityTuning::From(heapParams[0].get()); if (systemCapacity && *systemCapacity) { for (size_t i = 0; i < systemCapacity->flexCount(); ++i) { resources.push_back(systemCapacity->m.values[i]); ALOGI("System Resource[%zu]{%u %d %jd}", i, systemCapacity->m.values[i].id, systemCapacity->m.values[i].kind, systemCapacity->m.values[i].amount); } } else { ALOGE("Failed to get C2ResourcesCapacityTuning"); return C2_BAD_VALUE; } // Get the excluded resource info. // The available resource should exclude this, if there are any. const C2ResourcesExcludedTuning* systemExcluded = C2ResourcesExcludedTuning::From(heapParams[1].get()); if (systemExcluded && *systemExcluded) { for (size_t i = 0; i < systemExcluded->flexCount(); ++i) { const C2SystemResourceStruct& resource = systemExcluded->m.values[i]; ALOGI("Excluded Resource[%zu]{%u %d %jd}", i, resource.id, resource.kind, resource.amount); uint64_t excluded = (resource.kind == CONST) ? resource.amount : 0; auto found = std::find_if(resources.begin(), resources.end(), [resource](const C2SystemResourceStruct& item) { return item.id == resource.id; }); if (found != resources.end()) { // Take off excluded resources from available resources. if (found->amount >= excluded) { found->amount -= excluded; } else { ALOGE("Excluded resources(%jd) can't be more than Available resources(%jd)", excluded, found->amount); return C2_BAD_VALUE; } } else { ALOGE("Failed to find the resource [%u]", resource.id); return C2_BAD_VALUE; } } } else { ALOGE("Failed to get C2ResourcesExcludedTuning"); return C2_BAD_VALUE; } } else if (c2err == C2_OK) { ALOGE("Expected query results for 2 params, but got %zu", heapParams.size()); return C2_BAD_VALUE; } else { ALOGE("Failed to query component store for system resources: %d", c2err); return c2err; } size_t index = 0; for (const auto& resource : resources) { ALOGI("Globally Available System Resource[%zu]{%u %d %jd}", index++, resource.id, resource.kind, resource.amount); } return c2err; } TEST_F(CCodecConfigTest, QuerySystemResources) { init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC); ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable)); std::vector resources; ASSERT_EQ(C2_OK, queryGlobalResources(mConfigurable, resources)); // Make sure that what we got from the query is the same as what was added. ASSERT_TRUE(validateSystemResources(resources)); } } // namespace android