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