1 /*
2 * Copyright 2019 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include "CCodecConfig.h"
18
19 #include <set>
20
21 #include <gtest/gtest.h>
22
23 #include <android_media_codec.h>
24
25 #include <codec2/hidl/1.0/Configurable.h>
26 #include <codec2/hidl/client.h>
27 #include <util/C2InterfaceHelper.h>
28
29 #include <media/stagefright/MediaCodecConstants.h>
30
31 namespace {
32
33 enum ExtendedC2ParamIndexKind : C2Param::type_index_t {
34 kParamIndexVendorInt32 = C2Param::TYPE_INDEX_VENDOR_START,
35 kParamIndexVendorInt64,
36 kParamIndexVendorString,
37 };
38
39 typedef C2PortParam<C2Info, C2Int32Value, kParamIndexVendorInt32> C2PortVendorInt32Info;
40 constexpr char C2_PARAMKEY_VENDOR_INT32[] = "example.int32";
41 constexpr char KEY_VENDOR_INT32[] = "vendor.example.int32.value";
42
43 typedef C2StreamParam<C2Info, C2Int64Value, kParamIndexVendorInt64> C2StreamVendorInt64Info;
44 constexpr char C2_PARAMKEY_VENDOR_INT64[] = "example.int64";
45 constexpr char KEY_VENDOR_INT64[] = "vendor.example.int64.value";
46
47 typedef C2PortParam<C2Info, C2StringValue, kParamIndexVendorString> C2PortVendorStringInfo;
48 constexpr char C2_PARAMKEY_VENDOR_STRING[] = "example.string";
49 constexpr char KEY_VENDOR_STRING[] = "vendor.example.string.value";
50
51 } // namespace
52
53 namespace android {
54
55 class CCodecConfigTest : public ::testing::Test {
56 public:
57 constexpr static int32_t kCodec2Int32 = 0xC0DEC2;
58 constexpr static int64_t kCodec2Int64 = 0xC0DEC2C0DEC2ll;
59 constexpr static char kCodec2Str[] = "codec2";
60
CCodecConfigTest()61 CCodecConfigTest()
62 : mReflector{std::make_shared<C2ReflectorHelper>()} {
63 initializeSystemResources();
64 }
65
init(C2Component::domain_t domain,C2Component::kind_t kind,const char * mediaType)66 void init(
67 C2Component::domain_t domain,
68 C2Component::kind_t kind,
69 const char *mediaType) {
70 sp<hardware::media::c2::V1_0::utils::CachedConfigurable> cachedConfigurable =
71 new hardware::media::c2::V1_0::utils::CachedConfigurable(
72 std::make_unique<Configurable>(mReflector, domain, kind, mediaType,
73 mSystemResources, mExcludedResources));
74 cachedConfigurable->init(std::make_shared<Cache>());
75 mConfigurable = std::make_shared<Codec2Client::Configurable>(cachedConfigurable);
76 }
77
78 struct Cache : public hardware::media::c2::V1_0::utils::ParameterCache {
validateandroid::CCodecConfigTest::Cache79 c2_status_t validate(const std::vector<std::shared_ptr<C2ParamDescriptor>>&) override {
80 return C2_OK;
81 }
82 };
83
84 class Configurable : public hardware::media::c2::V1_0::utils::ConfigurableC2Intf {
85 public:
Configurable(const std::shared_ptr<C2ReflectorHelper> & reflector,C2Component::domain_t domain,C2Component::kind_t kind,const char * mediaType,const std::vector<C2SystemResourceStruct> & systemResources,const std::vector<C2SystemResourceStruct> & excludedResources)86 Configurable(
87 const std::shared_ptr<C2ReflectorHelper> &reflector,
88 C2Component::domain_t domain,
89 C2Component::kind_t kind,
90 const char *mediaType,
91 const std::vector<C2SystemResourceStruct>& systemResources,
92 const std::vector<C2SystemResourceStruct>& excludedResources)
93 : ConfigurableC2Intf("name", 0u),
94 mImpl(reflector, domain, kind, mediaType, systemResources, excludedResources) {
95 }
96
query(const std::vector<C2Param::Index> & indices,c2_blocking_t mayBlock,std::vector<std::unique_ptr<C2Param>> * const params) const97 c2_status_t query(
98 const std::vector<C2Param::Index> &indices,
99 c2_blocking_t mayBlock,
100 std::vector<std::unique_ptr<C2Param>>* const params) const override {
101 return mImpl.query({}, indices, mayBlock, params);
102 }
103
config(const std::vector<C2Param * > & params,c2_blocking_t mayBlock,std::vector<std::unique_ptr<C2SettingResult>> * const failures)104 c2_status_t config(
105 const std::vector<C2Param*> ¶ms,
106 c2_blocking_t mayBlock,
107 std::vector<std::unique_ptr<C2SettingResult>>* const failures) override {
108 return mImpl.config(params, mayBlock, failures);
109 }
110
querySupportedParams(std::vector<std::shared_ptr<C2ParamDescriptor>> * const params) const111 c2_status_t querySupportedParams(
112 std::vector<std::shared_ptr<C2ParamDescriptor>>* const params) const override {
113 return mImpl.querySupportedParams(params);
114 }
115
querySupportedValues(std::vector<C2FieldSupportedValuesQuery> & fields,c2_blocking_t mayBlock) const116 c2_status_t querySupportedValues(
117 std::vector<C2FieldSupportedValuesQuery>& fields,
118 c2_blocking_t mayBlock) const override {
119 return mImpl.querySupportedValues(fields, mayBlock);
120 }
121
122 private:
123 class Impl : public C2InterfaceHelper {
124 public:
Impl(const std::shared_ptr<C2ReflectorHelper> & reflector,C2Component::domain_t domain,C2Component::kind_t kind,const char * mediaType,const std::vector<C2SystemResourceStruct> & systemResources,const std::vector<C2SystemResourceStruct> & excludedResources)125 Impl(const std::shared_ptr<C2ReflectorHelper> &reflector,
126 C2Component::domain_t domain,
127 C2Component::kind_t kind,
128 const char *mediaType,
129 const std::vector<C2SystemResourceStruct>& systemResources,
130 const std::vector<C2SystemResourceStruct>& excludedResources)
131 : C2InterfaceHelper{reflector} {
132
133 setDerivedInstance(this);
134
135 addParameter(
136 DefineParam(mDomain, C2_PARAMKEY_COMPONENT_DOMAIN)
137 .withConstValue(new C2ComponentDomainSetting(domain))
138 .build());
139
140 addParameter(
141 DefineParam(mKind, C2_PARAMKEY_COMPONENT_KIND)
142 .withConstValue(new C2ComponentKindSetting(kind))
143 .build());
144
145 addParameter(
146 DefineParam(mInputStreamCount, C2_PARAMKEY_INPUT_STREAM_COUNT)
147 .withConstValue(new C2PortStreamCountTuning::input(1))
148 .build());
149
150 addParameter(
151 DefineParam(mOutputStreamCount, C2_PARAMKEY_OUTPUT_STREAM_COUNT)
152 .withConstValue(new C2PortStreamCountTuning::output(1))
153 .build());
154
155 const char *rawMediaType = "";
156 switch (domain) {
157 case C2Component::DOMAIN_IMAGE: [[fallthrough]];
158 case C2Component::DOMAIN_VIDEO:
159 rawMediaType = MIMETYPE_VIDEO_RAW;
160 break;
161 case C2Component::DOMAIN_AUDIO:
162 rawMediaType = MIMETYPE_AUDIO_RAW;
163 break;
164 default:
165 break;
166 }
167 bool isEncoder = kind == C2Component::KIND_ENCODER;
168 std::string inputMediaType{isEncoder ? rawMediaType : mediaType};
169 std::string outputMediaType{isEncoder ? mediaType : rawMediaType};
170
__anon243c35b90202(const auto ¶m, const std::string &str) 171 auto allocSharedString = [](const auto ¶m, const std::string &str) {
172 typedef typename std::remove_reference<decltype(param)>::type::element_type T;
173 std::shared_ptr<T> ret = T::AllocShared(str.length() + 1);
174 strcpy(ret->m.value, str.c_str());
175 return ret;
176 };
177
178 addParameter(
179 DefineParam(mInputMediaType, C2_PARAMKEY_INPUT_MEDIA_TYPE)
180 .withConstValue(allocSharedString(mInputMediaType, inputMediaType))
181 .build());
182
183 addParameter(
184 DefineParam(mOutputMediaType, C2_PARAMKEY_OUTPUT_MEDIA_TYPE)
185 .withConstValue(allocSharedString(mOutputMediaType, outputMediaType))
186 .build());
187
188 addParameter(
189 DefineParam(mInt32Input, C2_PARAMKEY_VENDOR_INT32)
190 .withDefault(new C2PortVendorInt32Info::input(0))
191 .withFields({C2F(mInt32Input, value).any()})
192 .withSetter(Setter<decltype(mInt32Input)::element_type>)
193 .build());
194
195 addParameter(
196 DefineParam(mInt64Output, C2_PARAMKEY_VENDOR_INT64)
197 .withDefault(new C2StreamVendorInt64Info::output(0u, 0))
198 .withFields({C2F(mInt64Output, value).any()})
199 .withSetter(Setter<decltype(mInt64Output)::element_type>)
200 .build());
201
202 addParameter(
203 DefineParam(mStringInput, C2_PARAMKEY_VENDOR_STRING)
204 .withDefault(decltype(mStringInput)::element_type::AllocShared(1, ""))
205 .withFields({C2F(mStringInput, m.value).any()})
206 .withSetter(Setter<decltype(mStringInput)::element_type>)
207 .build());
208
209 addParameter(
210 DefineParam(mPixelAspectRatio, C2_PARAMKEY_PIXEL_ASPECT_RATIO)
211 .withDefault(new C2StreamPixelAspectRatioInfo::output(0u, 1, 1))
212 .withFields({
213 C2F(mPixelAspectRatio, width).any(),
214 C2F(mPixelAspectRatio, height).any(),
215 })
216 .withSetter(Setter<C2StreamPixelAspectRatioInfo::output>)
217 .build());
218
219 // Add System Resource Capacity
220 addParameter(
221 DefineParam(mResourcesCapacity, C2_PARAMKEY_RESOURCES_CAPACITY)
222 .withDefault(C2ResourcesCapacityTuning::AllocShared(
223 systemResources.size(), systemResources))
224 .withFields({
225 C2F(mResourcesCapacity, m.values[0].id).any(),
226 C2F(mResourcesCapacity, m.values[0].kind).any(),
227 C2F(mResourcesCapacity, m.values[0].amount).any(),
228 })
229 .withSetter(Setter<C2ResourcesCapacityTuning>)
230 .build());
231
232 // Add Excluded System Resources
233 addParameter(
234 DefineParam(mResourcesExcluded, C2_PARAMKEY_RESOURCES_EXCLUDED)
235 .withDefault(C2ResourcesExcludedTuning::AllocShared(
236 excludedResources.size(), excludedResources))
237 .withFields({
238 C2F(mResourcesExcluded, m.values[0].id).any(),
239 C2F(mResourcesExcluded, m.values[0].kind).any(),
240 C2F(mResourcesExcluded, m.values[0].amount).any(),
241 })
242 .withSetter(Setter<C2ResourcesExcludedTuning>)
243 .build());
244
245 if (isEncoder) {
246 addParameter(
247 DefineParam(mInputBitrate, C2_PARAMKEY_BITRATE)
248 .withDefault(new C2StreamBitrateInfo::input(0u))
249 .withFields({C2F(mInputBitrate, value).any()})
250 .withSetter(Setter<C2StreamBitrateInfo::input>)
251 .build());
252
253 addParameter(
254 DefineParam(mOutputBitrate, C2_PARAMKEY_BITRATE)
255 .withDefault(new C2StreamBitrateInfo::output(0u))
256 .withFields({C2F(mOutputBitrate, value).any()})
257 .calculatedAs(
258 Copy<C2StreamBitrateInfo::output, C2StreamBitrateInfo::input>,
259 mInputBitrate)
260 .build());
261
262 addParameter(
263 DefineParam(mOutputProfileLevel, C2_PARAMKEY_PROFILE_LEVEL)
264 .withDefault(new C2StreamProfileLevelInfo::output(
265 0u, PROFILE_UNUSED, LEVEL_UNUSED))
266 .withFields({
267 C2F(mOutputProfileLevel, profile).any(),
268 C2F(mOutputProfileLevel, level).any(),
269 })
270 .withSetter(Setter<C2StreamProfileLevelInfo::output>)
271 .build());
272
273 std::vector<C2QpOffsetRectStruct> c2QpOffsetRectsInfo;
274 addParameter(
275 DefineParam(mInputQpOffsetRects, C2_PARAMKEY_QP_OFFSET_RECTS)
276 .withDefault(C2StreamQpOffsetRects::output::AllocShared(
277 c2QpOffsetRectsInfo.size(), 0, c2QpOffsetRectsInfo))
278 .withFields({
279 C2F(mInputQpOffsetRects, m.values[0].qpOffset)
280 .inRange(-128, 127),
281 C2F(mInputQpOffsetRects, m.values[0].left).any(),
282 C2F(mInputQpOffsetRects, m.values[0].top).any(),
283 C2F(mInputQpOffsetRects, m.values[0].width).any(),
284 C2F(mInputQpOffsetRects, m.values[0].height).any(),
285 })
286 .withSetter(Setter<C2StreamQpOffsetRects::output>)
287 .build());
288 }
289
290 // TODO: more SDK params
291 }
292 private:
293 std::shared_ptr<C2ComponentDomainSetting> mDomain;
294 std::shared_ptr<C2ComponentKindSetting> mKind;
295 std::shared_ptr<C2PortStreamCountTuning::input> mInputStreamCount;
296 std::shared_ptr<C2PortStreamCountTuning::output> mOutputStreamCount;
297 std::shared_ptr<C2PortMediaTypeSetting::input> mInputMediaType;
298 std::shared_ptr<C2PortMediaTypeSetting::output> mOutputMediaType;
299 std::shared_ptr<C2PortVendorInt32Info::input> mInt32Input;
300 std::shared_ptr<C2StreamVendorInt64Info::output> mInt64Output;
301 std::shared_ptr<C2PortVendorStringInfo::input> mStringInput;
302 std::shared_ptr<C2StreamPixelAspectRatioInfo::output> mPixelAspectRatio;
303 std::shared_ptr<C2StreamBitrateInfo::input> mInputBitrate;
304 std::shared_ptr<C2StreamBitrateInfo::output> mOutputBitrate;
305 std::shared_ptr<C2StreamProfileLevelInfo::input> mInputProfileLevel;
306 std::shared_ptr<C2StreamProfileLevelInfo::output> mOutputProfileLevel;
307 std::shared_ptr<C2StreamQpOffsetRects::output> mInputQpOffsetRects;
308 std::shared_ptr<C2ResourcesCapacityTuning> mResourcesCapacity;
309 std::shared_ptr<C2ResourcesExcludedTuning> mResourcesExcluded;
310
311 template<typename T>
Setter(bool,C2P<T> &)312 static C2R Setter(bool, C2P<T> &) {
313 return C2R::Ok();
314 }
315
316 template<typename ME, typename DEP>
Copy(bool,C2P<ME> & me,const C2P<DEP> & dep)317 static C2R Copy(bool, C2P<ME> &me, const C2P<DEP> &dep) {
318 me.set().value = dep.v.value;
319 return C2R::Ok();
320 }
321 };
322
323 Impl mImpl;
324 };
325
326 std::shared_ptr<C2ReflectorHelper> mReflector;
327 std::shared_ptr<Codec2Client::Configurable> mConfigurable;
328 CCodecConfig mConfig;
329
330 /*
331 * This test tracks two system resources:
332 * - max instance limit, which is capped at 64
333 * - max pixel count: up to 4 instances of 4K ==> 4 * 3840 * 2400
334 *
335 * These 2 resource types are given 2 different ids as below.
336 */
initializeSystemResources()337 void initializeSystemResources() {
338 // max instance limit 64
339 const uint32_t kMaxInstanceCount = 0x1000;
340 // max pixel count: up to 4 instances of 4K
341 const uint32_t kMaxPixelCount = 0x1001;
342 mSystemResources.push_back(C2SystemResourceStruct(kMaxInstanceCount, CONST, 64));
343 mSystemResources.push_back(C2SystemResourceStruct(kMaxPixelCount, CONST, 4 * 3840 * 2400));
344
345 // Nothing is excluded, but lets just add them with amount as 0.
346 mExcludedResources.push_back(C2SystemResourceStruct(kMaxInstanceCount, CONST, 0));
347 mExcludedResources.push_back(C2SystemResourceStruct(kMaxPixelCount, CONST, 0));
348 }
349
validateSystemResources(const std::vector<C2SystemResourceStruct> & resources) const350 bool validateSystemResources(const std::vector<C2SystemResourceStruct>& resources) const {
351 if (resources.size() != mSystemResources.size()) {
352 return false;
353 }
354
355 for (const auto& resource : mSystemResources) {
356 auto found = std::find_if(resources.begin(),
357 resources.end(),
358 [resource](const C2SystemResourceStruct& item) {
359 return (item.id == resource.id &&
360 item.kind == resource.kind &&
361 item.amount == resource.amount); });
362
363 if (found == resources.end()) {
364 return false;
365 }
366 }
367
368 return true;
369 }
370
371 private:
372 std::vector<C2SystemResourceStruct> mSystemResources;
373 std::vector<C2SystemResourceStruct> mExcludedResources;
374 };
375
376 using D = CCodecConfig::Domain;
377
378 template<typename T>
FindParam(const std::vector<std::unique_ptr<C2Param>> & vec)379 T *FindParam(const std::vector<std::unique_ptr<C2Param>> &vec) {
380 for (const std::unique_ptr<C2Param> ¶m : vec) {
381 if (param->coreIndex() == T::CORE_INDEX) {
382 return static_cast<T *>(param.get());
383 }
384 }
385 return nullptr;
386 }
387
TEST_F(CCodecConfigTest,SetVendorParam)388 TEST_F(CCodecConfigTest, SetVendorParam) {
389 // Test at audio domain, as video domain has a few local parameters that
390 // interfere with the testing.
391 init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
392
393 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
394
395 sp<AMessage> format{new AMessage};
396 format->setInt32(KEY_VENDOR_INT32, kCodec2Int32);
397 format->setInt64(KEY_VENDOR_INT64, kCodec2Int64);
398 format->setString(KEY_VENDOR_STRING, kCodec2Str);
399
400 std::vector<std::unique_ptr<C2Param>> configUpdate;
401 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
402 mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
403
404 ASSERT_EQ(3u, configUpdate.size());
405 C2PortVendorInt32Info::input *i32 =
406 FindParam<std::remove_pointer<decltype(i32)>::type>(configUpdate);
407 ASSERT_NE(nullptr, i32);
408 ASSERT_EQ(kCodec2Int32, i32->value);
409
410 C2StreamVendorInt64Info::output *i64 =
411 FindParam<std::remove_pointer<decltype(i64)>::type>(configUpdate);
412 ASSERT_NE(nullptr, i64);
413 ASSERT_EQ(kCodec2Int64, i64->value);
414
415 C2PortVendorStringInfo::input *str =
416 FindParam<std::remove_pointer<decltype(str)>::type>(configUpdate);
417 ASSERT_NE(nullptr, str);
418 ASSERT_STREQ(kCodec2Str, str->m.value);
419 }
420
TEST_F(CCodecConfigTest,VendorParamUpdate_Unsubscribed)421 TEST_F(CCodecConfigTest, VendorParamUpdate_Unsubscribed) {
422 // Test at audio domain, as video domain has a few local parameters that
423 // interfere with the testing.
424 init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
425
426 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
427
428 std::vector<std::unique_ptr<C2Param>> configUpdate;
429 C2PortVendorInt32Info::input i32(kCodec2Int32);
430 C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
431 std::unique_ptr<C2PortVendorStringInfo::input> str =
432 C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
433 configUpdate.push_back(C2Param::Copy(i32));
434 configUpdate.push_back(C2Param::Copy(i64));
435 configUpdate.push_back(std::move(str));
436
437 // The vendor parameters are not yet subscribed
438 ASSERT_FALSE(mConfig.updateConfiguration(configUpdate, D::ALL));
439
440 int32_t vendorInt32{0};
441 ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
442 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
443 ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
444 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
445
446 int64_t vendorInt64{0};
447 ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
448 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
449 ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
450 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
451
452 AString vendorString;
453 ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
454 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
455 ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
456 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
457 }
458
TEST_F(CCodecConfigTest,VendorParamUpdate_AllSubscribed)459 TEST_F(CCodecConfigTest, VendorParamUpdate_AllSubscribed) {
460 // Test at audio domain, as video domain has a few local parameters that
461 // interfere with the testing.
462 init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
463
464 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
465
466 // Force subscribe to all vendor params
467 ASSERT_EQ(OK, mConfig.subscribeToAllVendorParams(mConfigurable, C2_MAY_BLOCK));
468
469 std::vector<std::unique_ptr<C2Param>> configUpdate;
470 C2PortVendorInt32Info::input i32(kCodec2Int32);
471 C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
472 std::unique_ptr<C2PortVendorStringInfo::input> str =
473 C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
474 configUpdate.push_back(C2Param::Copy(i32));
475 configUpdate.push_back(C2Param::Copy(i64));
476 configUpdate.push_back(std::move(str));
477
478 ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
479
480 int32_t vendorInt32{0};
481 ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
482 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
483 ASSERT_EQ(kCodec2Int32, vendorInt32);
484 ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
485 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
486
487 int64_t vendorInt64{0};
488 ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
489 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
490 ASSERT_TRUE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
491 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
492 ASSERT_EQ(kCodec2Int64, vendorInt64);
493
494 AString vendorString;
495 ASSERT_TRUE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
496 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
497 ASSERT_STREQ(kCodec2Str, vendorString.c_str());
498 ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
499 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
500 }
501
TEST_F(CCodecConfigTest,VendorParamUpdate_PartiallySubscribed)502 TEST_F(CCodecConfigTest, VendorParamUpdate_PartiallySubscribed) {
503 // Test at audio domain, as video domain has a few local parameters that
504 // interfere with the testing.
505 init(C2Component::DOMAIN_AUDIO, C2Component::KIND_DECODER, MIMETYPE_AUDIO_AAC);
506
507 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
508
509 // Subscribe to example.int32 only
510 std::vector<std::unique_ptr<C2Param>> configUpdate;
511 sp<AMessage> format{new AMessage};
512 format->setInt32(KEY_VENDOR_INT32, 0);
513 configUpdate.clear();
514 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
515 mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
516 ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK));
517
518 C2PortVendorInt32Info::input i32(kCodec2Int32);
519 C2StreamVendorInt64Info::output i64(0u, kCodec2Int64);
520 std::unique_ptr<C2PortVendorStringInfo::input> str =
521 C2PortVendorStringInfo::input::AllocUnique(strlen(kCodec2Str) + 1, kCodec2Str);
522 configUpdate.clear();
523 configUpdate.push_back(C2Param::Copy(i32));
524 configUpdate.push_back(C2Param::Copy(i64));
525 configUpdate.push_back(std::move(str));
526
527 // Only example.i32 should be updated
528 ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
529
530 int32_t vendorInt32{0};
531 ASSERT_TRUE(mConfig.mInputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
532 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
533 ASSERT_EQ(kCodec2Int32, vendorInt32);
534 ASSERT_FALSE(mConfig.mOutputFormat->findInt32(KEY_VENDOR_INT32, &vendorInt32))
535 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
536
537 int64_t vendorInt64{0};
538 ASSERT_FALSE(mConfig.mInputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
539 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
540 ASSERT_FALSE(mConfig.mOutputFormat->findInt64(KEY_VENDOR_INT64, &vendorInt64))
541 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
542
543 AString vendorString;
544 ASSERT_FALSE(mConfig.mInputFormat->findString(KEY_VENDOR_STRING, &vendorString))
545 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
546 ASSERT_FALSE(mConfig.mOutputFormat->findString(KEY_VENDOR_STRING, &vendorString))
547 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
548 }
549
TEST_F(CCodecConfigTest,SetPixelAspectRatio)550 TEST_F(CCodecConfigTest, SetPixelAspectRatio) {
551 init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC);
552
553 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
554
555 sp<AMessage> format{new AMessage};
556 format->setInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, 12);
557 format->setInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, 11);
558
559 std::vector<std::unique_ptr<C2Param>> configUpdate;
560 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
561 mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
562
563 ASSERT_EQ(1u, configUpdate.size());
564 C2StreamPixelAspectRatioInfo::output *par =
565 FindParam<std::remove_pointer<decltype(par)>::type>(configUpdate);
566 ASSERT_NE(nullptr, par);
567 ASSERT_EQ(12, par->width);
568 ASSERT_EQ(11, par->height);
569 }
570
TEST_F(CCodecConfigTest,PixelAspectRatioUpdate)571 TEST_F(CCodecConfigTest, PixelAspectRatioUpdate) {
572 init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC);
573
574 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
575
576 std::vector<std::unique_ptr<C2Param>> configUpdate;
577 C2StreamPixelAspectRatioInfo::output par(0u, 12, 11);
578 configUpdate.push_back(C2Param::Copy(par));
579
580 ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
581
582 int32_t parWidth{0};
583 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, &parWidth))
584 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
585 ASSERT_EQ(12, parWidth);
586 ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_WIDTH, &parWidth))
587 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
588
589 int32_t parHeight{0};
590 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, &parHeight))
591 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
592 ASSERT_EQ(11, parHeight);
593 ASSERT_FALSE(mConfig.mInputFormat->findInt32(KEY_PIXEL_ASPECT_RATIO_HEIGHT, &parHeight))
594 << "mInputFormat = " << mConfig.mInputFormat->debugString().c_str();
595 }
596
TEST_F(CCodecConfigTest,DataspaceUpdate)597 TEST_F(CCodecConfigTest, DataspaceUpdate) {
598 init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, MIMETYPE_VIDEO_AVC);
599
600 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
601 class InputSurfaceStub : public InputSurfaceWrapper {
602 public:
603 ~InputSurfaceStub() override = default;
604 status_t connect(const std::shared_ptr<Codec2Client::Component> &) override {
605 return OK;
606 }
607 void disconnect() override {}
608 status_t start() override { return OK; }
609 status_t signalEndOfInputStream() override { return OK; }
610 status_t configure(Config &) override { return OK; }
611 };
612 mConfig.mInputSurface = std::make_shared<InputSurfaceStub>();
613
614 sp<AMessage> format{new AMessage};
615 format->setInt32(KEY_COLOR_RANGE, COLOR_RANGE_LIMITED);
616 format->setInt32(KEY_COLOR_STANDARD, COLOR_STANDARD_BT709);
617 format->setInt32(KEY_COLOR_TRANSFER, COLOR_TRANSFER_SDR_VIDEO);
618 format->setInt32(KEY_BIT_RATE, 100);
619
620 std::vector<std::unique_ptr<C2Param>> configUpdate;
621 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
622 mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
623 ASSERT_TRUE(mConfig.updateConfiguration(configUpdate, D::ALL));
624
625 int32_t range{0};
626 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range))
627 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
628 EXPECT_EQ(COLOR_RANGE_LIMITED, range)
629 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
630
631 int32_t standard{0};
632 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard))
633 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
634 EXPECT_EQ(COLOR_STANDARD_BT709, standard)
635 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
636
637 int32_t transfer{0};
638 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer))
639 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
640 EXPECT_EQ(COLOR_TRANSFER_SDR_VIDEO, transfer)
641 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
642
643 mConfig.mInputSurface->setDataSpace(HAL_DATASPACE_BT2020_PQ);
644
645 // Dataspace from input surface should override the configured setting
646 mConfig.updateFormats(D::ALL);
647
648 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range))
649 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
650 EXPECT_EQ(COLOR_RANGE_FULL, range)
651 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
652
653 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard))
654 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
655 EXPECT_EQ(COLOR_STANDARD_BT2020, standard)
656 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
657
658 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer))
659 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
660 EXPECT_EQ(COLOR_TRANSFER_ST2084, transfer)
661 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
662
663 // Simulate bitrate update
664 format = new AMessage;
665 format->setInt32(KEY_BIT_RATE, 200);
666 configUpdate.clear();
667 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
668 mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
669 ASSERT_EQ(OK, mConfig.setParameters(mConfigurable, configUpdate, C2_MAY_BLOCK));
670
671 // Color information should remain the same
672 mConfig.updateFormats(D::ALL);
673
674 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_RANGE, &range))
675 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
676 EXPECT_EQ(COLOR_RANGE_FULL, range)
677 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
678
679 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_STANDARD, &standard))
680 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
681 EXPECT_EQ(COLOR_STANDARD_BT2020, standard)
682 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
683
684 ASSERT_TRUE(mConfig.mOutputFormat->findInt32(KEY_COLOR_TRANSFER, &transfer))
685 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
686 EXPECT_EQ(COLOR_TRANSFER_ST2084, transfer)
687 << "mOutputFormat = " << mConfig.mOutputFormat->debugString().c_str();
688 }
689
690 typedef std::tuple<std::string, C2Config::profile_t, int32_t> HdrProfilesParams;
691
692 class HdrProfilesTest
693 : public CCodecConfigTest,
694 public ::testing::WithParamInterface<HdrProfilesParams> {
695 };
696
TEST_P(HdrProfilesTest,SetFromSdk)697 TEST_P(HdrProfilesTest, SetFromSdk) {
698 HdrProfilesParams params = GetParam();
699 std::string mediaType = std::get<0>(params);
700 C2Config::profile_t c2Profile = std::get<1>(params);
701 int32_t sdkProfile = std::get<2>(params);
702
703 init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, mediaType.c_str());
704
705 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
706
707 sp<AMessage> format{new AMessage};
708 format->setInt32(KEY_PROFILE, sdkProfile);
709
710 std::vector<std::unique_ptr<C2Param>> configUpdate;
711 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(
712 mConfigurable, format, D::ALL, C2_MAY_BLOCK, &configUpdate));
713
714 ASSERT_EQ(1u, configUpdate.size());
715 C2StreamProfileLevelInfo::input *pl =
716 FindParam<std::remove_pointer<decltype(pl)>::type>(configUpdate);
717 ASSERT_NE(nullptr, pl);
718 ASSERT_EQ(c2Profile, pl->profile);
719 }
720
721 HdrProfilesParams kHdrProfilesParams[] = {
722 std::make_tuple(MIMETYPE_VIDEO_HEVC, PROFILE_HEVC_MAIN_10, HEVCProfileMain10HDR10),
723 std::make_tuple(MIMETYPE_VIDEO_HEVC, PROFILE_HEVC_MAIN_10, HEVCProfileMain10HDR10Plus),
724 std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_2, VP9Profile2HDR),
725 std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_2, VP9Profile2HDR10Plus),
726 std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_3, VP9Profile3HDR),
727 std::make_tuple(MIMETYPE_VIDEO_VP9, PROFILE_VP9_3, VP9Profile3HDR10Plus),
728 std::make_tuple(MIMETYPE_VIDEO_AV1, PROFILE_AV1_0, AV1ProfileMain10HDR10),
729 std::make_tuple(MIMETYPE_VIDEO_AV1, PROFILE_AV1_0, AV1ProfileMain10HDR10Plus),
730 };
731
732 INSTANTIATE_TEST_SUITE_P(
733 CCodecConfig,
734 HdrProfilesTest,
735 ::testing::ValuesIn(kHdrProfilesParams));
736
TEST_F(CCodecConfigTest,SetRegionOfInterestParams)737 TEST_F(CCodecConfigTest, SetRegionOfInterestParams) {
738 if (!android::media::codec::provider_->region_of_interest()
739 || !android::media::codec::provider_->region_of_interest_support()) {
740 GTEST_SKIP() << "Skipping the test as region_of_interest flags are not enabled.\n";
741 }
742
743 init(C2Component::DOMAIN_VIDEO, C2Component::KIND_ENCODER, MIMETYPE_VIDEO_VP9);
744
745 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
746
747 const int kWidth = 32;
748 const int kHeight = 32;
749 const int kNumBlocks = ((kWidth + 15) / 16) * ((kHeight + 15) / 16);
750 int8_t mapInfo[kNumBlocks] = {-1, 0, 1, 1};
751 int top[kNumBlocks] = {0, 0, 16, 16};
752 int left[kNumBlocks] = {0, 16, 0, 16};
753 int bottom[kNumBlocks] = {16, 16, 32, 32};
754 int right[kNumBlocks] = {16, 32, 16, 32};
755 sp<AMessage> format{new AMessage};
756 format->setInt32(KEY_WIDTH, kWidth);
757 format->setInt32(KEY_HEIGHT, kHeight);
758 AString val;
759 for (int i = 0; i < kNumBlocks; i++) {
760 val.append(AStringPrintf("%d,%d-%d,%d=%d;", top[i], left[i], bottom[i],
761 right[i], mapInfo[i]));
762 }
763 format->setString(PARAMETER_KEY_QP_OFFSET_RECTS, val);
764
765 std::vector<std::unique_ptr<C2Param>> configUpdate;
766 ASSERT_EQ(OK, mConfig.getConfigUpdateFromSdkParams(mConfigurable, format, D::CONFIG,
767 C2_MAY_BLOCK, &configUpdate));
768
769 EXPECT_EQ(1u, configUpdate.size());
770
771 C2StreamQpOffsetRects::output* qpRectParam =
772 FindParam<std::remove_pointer<decltype(qpRectParam)>::type>(configUpdate);
773 ASSERT_NE(nullptr, qpRectParam);
774 ASSERT_EQ(kNumBlocks, qpRectParam->flexCount());
775 for (auto i = 0; i < kNumBlocks; i++) {
776 EXPECT_EQ(mapInfo[i], (int8_t)qpRectParam->m.values[i].qpOffset)
777 << "qp offset for index " << i << " is not as expected ";
778 EXPECT_EQ(left[i], qpRectParam->m.values[i].left)
779 << "left for index " << i << " is not as expected ";
780 EXPECT_EQ(top[i], qpRectParam->m.values[i].top)
781 << "top for index " << i << " is not as expected ";
782 EXPECT_EQ(right[i] - left[i], qpRectParam->m.values[i].width)
783 << "width for index " << i << " is not as expected ";
784 EXPECT_EQ(bottom[i] - top[i], qpRectParam->m.values[i].height)
785 << "height for index " << i << " is not as expected ";
786 }
787 }
788
789 static
queryGlobalResources(std::shared_ptr<Codec2Client::Configurable> & configurable,std::vector<C2SystemResourceStruct> & resources)790 c2_status_t queryGlobalResources(std::shared_ptr<Codec2Client::Configurable>& configurable,
791 std::vector<C2SystemResourceStruct>& resources) {
792 std::vector<std::unique_ptr<C2Param>> heapParams;
793 c2_status_t c2err = configurable->query(
794 {},
795 {C2ResourcesCapacityTuning::PARAM_TYPE, C2ResourcesExcludedTuning::PARAM_TYPE},
796 C2_MAY_BLOCK, &heapParams);
797
798 if (c2err == C2_OK && heapParams.size() == 2u) {
799 // Construct Globally available resources now.
800 // Get the total capacity first.
801 const C2ResourcesCapacityTuning* systemCapacity =
802 C2ResourcesCapacityTuning::From(heapParams[0].get());
803 if (systemCapacity && *systemCapacity) {
804 for (size_t i = 0; i < systemCapacity->flexCount(); ++i) {
805 resources.push_back(systemCapacity->m.values[i]);
806 ALOGI("System Resource[%zu]{%u %d %jd}", i,
807 systemCapacity->m.values[i].id,
808 systemCapacity->m.values[i].kind,
809 systemCapacity->m.values[i].amount);
810 }
811 } else {
812 ALOGE("Failed to get C2ResourcesCapacityTuning");
813 return C2_BAD_VALUE;
814 }
815
816 // Get the excluded resource info.
817 // The available resource should exclude this, if there are any.
818 const C2ResourcesExcludedTuning* systemExcluded =
819 C2ResourcesExcludedTuning::From(heapParams[1].get());
820 if (systemExcluded && *systemExcluded) {
821 for (size_t i = 0; i < systemExcluded->flexCount(); ++i) {
822 const C2SystemResourceStruct& resource =
823 systemExcluded->m.values[i];
824 ALOGI("Excluded Resource[%zu]{%u %d %jd}", i,
825 resource.id, resource.kind, resource.amount);
826 uint64_t excluded = (resource.kind == CONST) ? resource.amount : 0;
827 auto found = std::find_if(resources.begin(),
828 resources.end(),
829 [resource](const C2SystemResourceStruct& item) {
830 return item.id == resource.id; });
831
832 if (found != resources.end()) {
833 // Take off excluded resources from available resources.
834 if (found->amount >= excluded) {
835 found->amount -= excluded;
836 } else {
837 ALOGE("Excluded resources(%jd) can't be more than Available resources(%jd)",
838 excluded, found->amount);
839 return C2_BAD_VALUE;
840 }
841 } else {
842 ALOGE("Failed to find the resource [%u]", resource.id);
843 return C2_BAD_VALUE;
844 }
845 }
846 } else {
847 ALOGE("Failed to get C2ResourcesExcludedTuning");
848 return C2_BAD_VALUE;
849 }
850
851 } else if (c2err == C2_OK) {
852 ALOGE("Expected query results for 2 params, but got %zu", heapParams.size());
853 return C2_BAD_VALUE;
854 } else {
855 ALOGE("Failed to query component store for system resources: %d", c2err);
856 return c2err;
857 }
858
859 size_t index = 0;
860 for (const auto& resource : resources) {
861 ALOGI("Globally Available System Resource[%zu]{%u %d %jd}", index++,
862 resource.id, resource.kind, resource.amount);
863 }
864 return c2err;
865 }
866
TEST_F(CCodecConfigTest,QuerySystemResources)867 TEST_F(CCodecConfigTest, QuerySystemResources) {
868 init(C2Component::DOMAIN_VIDEO, C2Component::KIND_DECODER, MIMETYPE_VIDEO_AVC);
869
870 ASSERT_EQ(OK, mConfig.initialize(mReflector, mConfigurable));
871
872 std::vector<C2SystemResourceStruct> resources;
873 ASSERT_EQ(C2_OK, queryGlobalResources(mConfigurable, resources));
874
875 // Make sure that what we got from the query is the same as what was added.
876 ASSERT_TRUE(validateSystemResources(resources));
877 }
878
879 } // namespace android
880