1 /*
2 * Copyright (C) 2018 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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "codec2_hidl_hal_component_test"
19
20 #include <android-base/logging.h>
21 #include <gtest/gtest.h>
22 #include <hidl/GtestPrinter.h>
23
24 #include <C2Config.h>
25 #include <codec2/hidl/client.h>
26
27 #include "media_c2_hidl_test_common.h"
28
29 /* Time_Out for start(), stop(), reset(), release(), flush(), queue() are
30 * defined in hardware/interfaces/media/c2/1.0/IComponent.hal. Adding 50ms
31 * extra in case of timeout is 500ms, 1ms extra in case timeout is 1ms/5ms. All
32 * timeout is calculated in us.
33 */
34 #define START_TIME_OUT 550000
35 #define STOP_TIME_OUT 550000
36 #define RESET_TIME_OUT 550000
37 #define RELEASE_TIME_OUT 550000
38 #define FLUSH_TIME_OUT 6000
39 #define QUEUE_TIME_OUT 2000
40
41 // Time_Out for config(), query(), querySupportedParams() are defined in
42 // hardware/interfaces/media/c2/1.0/IConfigurable.hal.
43 #define CONFIG_TIME_OUT 6000
44 #define QUERY_TIME_OUT 6000
45 #define QUERYSUPPORTEDPARAMS_TIME_OUT 2000
46
47 #define CHECK_TIMEOUT(timeConsumed, TIME_OUT, FuncName) \
48 if (timeConsumed > TIME_OUT) { \
49 ALOGW("TIMED_OUT %s timeConsumed=%" PRId64 \
50 " us is " \
51 "greater than threshold %d us", \
52 FuncName, timeConsumed, TIME_OUT); \
53 }
54
55 namespace {
56 using InputTestParameters = std::tuple<std::string, std::string, uint32_t, bool>;
57 static std::vector<InputTestParameters> gInputTestParameters;
58
59 // google.codec2 Component test setup
60 class Codec2ComponentHidlTestBase : public ::testing::Test {
61 public:
SetUp()62 virtual void SetUp() override {
63 getParams();
64 mEos = false;
65 mClient = android::Codec2Client::CreateFromService(mInstanceName.c_str());
66 ASSERT_NE(mClient, nullptr);
67 mListener.reset(new CodecListener([this](std::list<std::unique_ptr<C2Work>>& workItems) {
68 handleWorkDone(workItems);
69 }));
70 ASSERT_NE(mListener, nullptr);
71 mClient->createComponent(mComponentName.c_str(), mListener, &mComponent);
72 ASSERT_NE(mComponent, nullptr);
73 for (int i = 0; i < MAX_INPUT_BUFFERS; ++i) {
74 mWorkQueue.emplace_back(new C2Work);
75 }
76 }
77
TearDown()78 virtual void TearDown() override {
79 if (mComponent != nullptr) {
80 // If you have encountered a fatal failure, it is possible that
81 // freeNode() will not go through. Instead of hanging the app.
82 // let it pass through and report errors
83 if (::testing::Test::HasFatalFailure()) return;
84 mComponent->release();
85 mComponent = nullptr;
86 }
87 }
88
89 // Get the test parameters from GetParam call.
getParams()90 virtual void getParams() {}
91
92 // callback function to process onWorkDone received by Listener
handleWorkDone(std::list<std::unique_ptr<C2Work>> & workItems)93 void handleWorkDone(std::list<std::unique_ptr<C2Work>>& workItems) {
94 for (std::unique_ptr<C2Work>& work : workItems) {
95 if (!work->worklets.empty()) {
96 bool mCsd = false;
97 uint32_t mFramesReceived = 0;
98 std::list<uint64_t> mFlushedIndices;
99 workDone(mComponent, work, mFlushedIndices, mQueueLock, mQueueCondition, mWorkQueue,
100 mEos, mCsd, mFramesReceived);
101 }
102 }
103 }
104
105 std::string mInstanceName;
106 std::string mComponentName;
107 bool mEos;
108 std::mutex mQueueLock;
109 std::condition_variable mQueueCondition;
110 std::list<std::unique_ptr<C2Work>> mWorkQueue;
111
112 std::shared_ptr<android::Codec2Client> mClient;
113 std::shared_ptr<android::Codec2Client::Listener> mListener;
114 std::shared_ptr<android::Codec2Client::Component> mComponent;
115
116 protected:
description(const std::string & description)117 static void description(const std::string& description) {
118 RecordProperty("description", description);
119 }
120 };
121
122 class Codec2ComponentHidlTest : public Codec2ComponentHidlTestBase,
123 public ::testing::WithParamInterface<TestParameters> {
getParams()124 void getParams() {
125 mInstanceName = std::get<0>(GetParam());
126 mComponentName = std::get<1>(GetParam());
127 }
128 };
129
130 // Test Empty Flush
TEST_P(Codec2ComponentHidlTest,EmptyFlush)131 TEST_P(Codec2ComponentHidlTest, EmptyFlush) {
132 ALOGV("Empty Flush Test");
133 c2_status_t err = mComponent->start();
134 ASSERT_EQ(err, C2_OK);
135
136 std::list<std::unique_ptr<C2Work>> flushedWork;
137 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &flushedWork);
138 ASSERT_EQ(err, C2_OK);
139
140 err = mComponent->stop();
141 ASSERT_EQ(err, C2_OK);
142 // Empty Flush should not return any work
143 ASSERT_EQ(flushedWork.size(), 0u);
144 }
145
146 // Test Queue Empty Work
TEST_P(Codec2ComponentHidlTest,QueueEmptyWork)147 TEST_P(Codec2ComponentHidlTest, QueueEmptyWork) {
148 ALOGV("Queue Empty Work Test");
149 c2_status_t err = mComponent->start();
150 ASSERT_EQ(err, C2_OK);
151
152 // Queueing an empty WorkBundle
153 std::list<std::unique_ptr<C2Work>> workList;
154 mComponent->queue(&workList);
155
156 err = mComponent->reset();
157 ASSERT_EQ(err, C2_OK);
158 }
159
160 // Test Component Configuration
TEST_P(Codec2ComponentHidlTest,Config)161 TEST_P(Codec2ComponentHidlTest, Config) {
162 ALOGV("Configuration Test");
163
164 C2String name = mComponent->getName();
165 EXPECT_NE(name.empty(), true) << "Invalid Component Name";
166
167 c2_status_t err = C2_OK;
168 std::vector<std::unique_ptr<C2Param>> queried;
169 std::vector<std::unique_ptr<C2SettingResult>> failures;
170
171 // Query supported params by the component
172 std::vector<std::shared_ptr<C2ParamDescriptor>> params;
173 err = mComponent->querySupportedParams(¶ms);
174 ASSERT_EQ(err, C2_OK);
175 ALOGV("Number of total params - %zu", params.size());
176
177 // Query and config all the supported params
178 for (std::shared_ptr<C2ParamDescriptor> p : params) {
179 ALOGD("Querying index %d", (int)p->index());
180 err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
181 EXPECT_NE(queried.size(), 0u);
182 EXPECT_EQ(err, C2_OK);
183 err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
184 ASSERT_EQ(err, C2_OK);
185 ASSERT_EQ(failures.size(), 0u);
186 }
187 }
188
189 // Test Multiple Start Stop Reset Test
TEST_P(Codec2ComponentHidlTest,MultipleStartStopReset)190 TEST_P(Codec2ComponentHidlTest, MultipleStartStopReset) {
191 ALOGV("Multiple Start Stop and Reset Test");
192
193 for (size_t i = 0; i < MAX_RETRY; i++) {
194 mComponent->start();
195 mComponent->stop();
196 }
197
198 ASSERT_EQ(mComponent->start(), C2_OK);
199
200 for (size_t i = 0; i < MAX_RETRY; i++) {
201 mComponent->reset();
202 }
203
204 ASSERT_EQ(mComponent->start(), C2_OK);
205 ASSERT_EQ(mComponent->stop(), C2_OK);
206
207 // Second stop should return error
208 ASSERT_NE(mComponent->stop(), C2_OK);
209 }
210
211 // Test Component Release API
TEST_P(Codec2ComponentHidlTest,MultipleRelease)212 TEST_P(Codec2ComponentHidlTest, MultipleRelease) {
213 ALOGV("Multiple Release Test");
214 c2_status_t err = mComponent->start();
215 ASSERT_EQ(err, C2_OK);
216
217 // Query Component Domain Type
218 std::vector<std::unique_ptr<C2Param>> queried;
219 err = mComponent->query({}, {C2PortMediaTypeSetting::input::PARAM_TYPE}, C2_DONT_BLOCK,
220 &queried);
221 EXPECT_NE(queried.size(), 0u);
222
223 // Configure Component Domain
224 std::vector<std::unique_ptr<C2SettingResult>> failures;
225 C2PortMediaTypeSetting::input* portMediaType =
226 C2PortMediaTypeSetting::input::From(queried[0].get());
227 err = mComponent->config({portMediaType}, C2_DONT_BLOCK, &failures);
228 ASSERT_EQ(err, C2_OK);
229 ASSERT_EQ(failures.size(), 0u);
230
231 for (size_t i = 0; i < MAX_RETRY; i++) {
232 mComponent->release();
233 }
234 }
235
236 // Test API's Timeout
TEST_P(Codec2ComponentHidlTest,Timeout)237 TEST_P(Codec2ComponentHidlTest, Timeout) {
238 ALOGV("Timeout Test");
239 c2_status_t err = C2_OK;
240
241 int64_t startTime = getNowUs();
242 err = mComponent->start();
243 int64_t timeConsumed = getNowUs() - startTime;
244 CHECK_TIMEOUT(timeConsumed, START_TIME_OUT, "start()");
245 ALOGV("mComponent->start() timeConsumed=%" PRId64 " us", timeConsumed);
246 ASSERT_EQ(err, C2_OK);
247
248 startTime = getNowUs();
249 err = mComponent->reset();
250 timeConsumed = getNowUs() - startTime;
251 CHECK_TIMEOUT(timeConsumed, RESET_TIME_OUT, "reset()");
252 ALOGV("mComponent->reset() timeConsumed=%" PRId64 " us", timeConsumed);
253 ASSERT_EQ(err, C2_OK);
254
255 err = mComponent->start();
256 ASSERT_EQ(err, C2_OK);
257
258 // Query supported params by the component
259 std::vector<std::shared_ptr<C2ParamDescriptor>> params;
260 startTime = getNowUs();
261 err = mComponent->querySupportedParams(¶ms);
262 timeConsumed = getNowUs() - startTime;
263 CHECK_TIMEOUT(timeConsumed, QUERYSUPPORTEDPARAMS_TIME_OUT, "querySupportedParams()");
264 ALOGV("mComponent->querySupportedParams() timeConsumed=%" PRId64 " us", timeConsumed);
265 ASSERT_EQ(err, C2_OK);
266
267 std::vector<std::unique_ptr<C2Param>> queried;
268 std::vector<std::unique_ptr<C2SettingResult>> failures;
269 // Query and config all the supported params
270 for (std::shared_ptr<C2ParamDescriptor> p : params) {
271 startTime = getNowUs();
272 err = mComponent->query({}, {p->index()}, C2_DONT_BLOCK, &queried);
273 timeConsumed = getNowUs() - startTime;
274 CHECK_TIMEOUT(timeConsumed, QUERY_TIME_OUT, "query()");
275 EXPECT_NE(queried.size(), 0u);
276 EXPECT_EQ(err, C2_OK);
277 ALOGV("mComponent->query() for %s timeConsumed=%" PRId64 " us", p->name().c_str(),
278 timeConsumed);
279
280 startTime = getNowUs();
281 err = mComponent->config({queried[0].get()}, C2_DONT_BLOCK, &failures);
282 timeConsumed = getNowUs() - startTime;
283 CHECK_TIMEOUT(timeConsumed, CONFIG_TIME_OUT, "config()");
284 ASSERT_EQ(err, C2_OK);
285 ASSERT_EQ(failures.size(), 0u);
286 ALOGV("mComponent->config() for %s timeConsumed=%" PRId64 " us", p->name().c_str(),
287 timeConsumed);
288 }
289
290 std::list<std::unique_ptr<C2Work>> workList;
291 startTime = getNowUs();
292 err = mComponent->queue(&workList);
293 timeConsumed = getNowUs() - startTime;
294 ALOGV("mComponent->queue() timeConsumed=%" PRId64 " us", timeConsumed);
295 CHECK_TIMEOUT(timeConsumed, QUEUE_TIME_OUT, "queue()");
296
297 startTime = getNowUs();
298 err = mComponent->flush(C2Component::FLUSH_COMPONENT, &workList);
299 timeConsumed = getNowUs() - startTime;
300 ALOGV("mComponent->flush() timeConsumed=%" PRId64 " us", timeConsumed);
301 CHECK_TIMEOUT(timeConsumed, FLUSH_TIME_OUT, "flush()");
302
303 startTime = getNowUs();
304 err = mComponent->stop();
305 timeConsumed = getNowUs() - startTime;
306 ALOGV("mComponent->stop() timeConsumed=%" PRId64 " us", timeConsumed);
307 CHECK_TIMEOUT(timeConsumed, STOP_TIME_OUT, "stop()");
308 ASSERT_EQ(err, C2_OK);
309
310 startTime = getNowUs();
311 err = mComponent->release();
312 timeConsumed = getNowUs() - startTime;
313 ALOGV("mComponent->release() timeConsumed=%" PRId64 " us", timeConsumed);
314 CHECK_TIMEOUT(timeConsumed, RELEASE_TIME_OUT, "release()");
315 ASSERT_EQ(err, C2_OK);
316 }
317
318 class Codec2ComponentInputTests : public Codec2ComponentHidlTestBase,
319 public ::testing::WithParamInterface<InputTestParameters> {
getParams()320 void getParams() {
321 mInstanceName = std::get<0>(GetParam());
322 mComponentName = std::get<1>(GetParam());
323 }
324 };
325
TEST_P(Codec2ComponentInputTests,InputBufferTest)326 TEST_P(Codec2ComponentInputTests, InputBufferTest) {
327 description("Tests for different inputs");
328
329 uint32_t flags = std::get<2>(GetParam());
330 bool isNullBuffer = std::get<3>(GetParam());
331 if (isNullBuffer)
332 ALOGD("Testing for null input buffer with flag : %u", flags);
333 else
334 ALOGD("Testing for empty input buffer with flag : %u", flags);
335 mEos = false;
336 ASSERT_EQ(mComponent->start(), C2_OK);
337 ASSERT_NO_FATAL_FAILURE(
338 testInputBuffer(mComponent, mQueueLock, mWorkQueue, flags, isNullBuffer));
339
340 ALOGD("Waiting for input consumption");
341 ASSERT_NO_FATAL_FAILURE(waitOnInputConsumption(mQueueLock, mQueueCondition, mWorkQueue));
342
343 if (flags == C2FrameData::FLAG_END_OF_STREAM) ASSERT_EQ(mEos, true);
344 ASSERT_EQ(mComponent->stop(), C2_OK);
345 ASSERT_EQ(mComponent->reset(), C2_OK);
346 }
347
348 INSTANTIATE_TEST_SUITE_P(PerInstance, Codec2ComponentHidlTest, testing::ValuesIn(gTestParameters),
349 PrintInstanceTupleNameToString<>);
350
351 INSTANTIATE_TEST_CASE_P(NonStdInputs, Codec2ComponentInputTests,
352 testing::ValuesIn(gInputTestParameters), PrintInstanceTupleNameToString<>);
353 } // anonymous namespace
354
355 // TODO: Add test for Invalid work,
356 // TODO: Add test for Invalid states
main(int argc,char ** argv)357 int main(int argc, char** argv) {
358 parseArgs(argc, argv);
359 gTestParameters = getTestParameters();
360 for (auto params : gTestParameters) {
361 gInputTestParameters.push_back(
362 std::make_tuple(std::get<0>(params), std::get<1>(params), 0, true));
363 gInputTestParameters.push_back(std::make_tuple(std::get<0>(params), std::get<1>(params),
364 C2FrameData::FLAG_END_OF_STREAM, true));
365 gInputTestParameters.push_back(
366 std::make_tuple(std::get<0>(params), std::get<1>(params), 0, false));
367 gInputTestParameters.push_back(std::make_tuple(std::get<0>(params), std::get<1>(params),
368 C2FrameData::FLAG_CODEC_CONFIG, false));
369 gInputTestParameters.push_back(std::make_tuple(std::get<0>(params), std::get<1>(params),
370 C2FrameData::FLAG_END_OF_STREAM, false));
371 }
372
373 ::testing::InitGoogleTest(&argc, argv);
374 return RUN_ALL_TESTS();
375 }
376