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(&params);
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(&params);
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