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 "media_c2_hidl_test_common"
19 #include <stdio.h>
20 
21 #include "media_c2_hidl_test_common.h"
22 
23 #include <android/hardware/media/c2/1.0/IComponentStore.h>
24 
25 std::string sResourceDir = "";
26 
27 std::string sComponentNamePrefix = "";
28 
29 static constexpr struct option kArgOptions[] = {
30         {"res", required_argument, 0, 'P'},
31         {"prefix", required_argument, 0, 'p'},
32         {"help", required_argument, 0, 'h'},
33         {nullptr, 0, nullptr, 0},
34 };
35 
printUsage(char * me)36 void printUsage(char* me) {
37     std::cerr << "VTS tests to test codec2 components \n";
38     std::cerr << "Usage: " << me << " [options] \n";
39     std::cerr << "\t -P,  --res:    Mandatory path to a folder that contains test resources \n";
40     std::cerr << "\t -p,  --prefix: Optional prefix to select component/s to be tested \n";
41     std::cerr << "\t                    All codecs are tested by default \n";
42     std::cerr << "\t                    Eg: c2.android - test codecs starting with c2.android \n";
43     std::cerr << "\t                    Eg: c2.android.aac.decoder - test a specific codec \n";
44     std::cerr << "\t -h,  --help:   Print usage \n";
45 }
46 
parseArgs(int argc,char ** argv)47 void parseArgs(int argc, char** argv) {
48     int arg;
49     int option_index;
50     while ((arg = getopt_long(argc, argv, ":P:p:h", kArgOptions, &option_index)) != -1) {
51         switch (arg) {
52             case 'P':
53                 sResourceDir = optarg;
54                 break;
55             case 'p':
56                 sComponentNamePrefix = optarg;
57                 break;
58             case 'h':
59                 printUsage(argv[0]);
60                 break;
61             default:
62                 break;
63         }
64     }
65 }
66 
67 // Test the codecs for NullBuffer, Empty Input Buffer with(out) flags set
testInputBuffer(const std::shared_ptr<android::Codec2Client::Component> & component,std::mutex & queueLock,std::list<std::unique_ptr<C2Work>> & workQueue,uint32_t flags,bool isNullBuffer)68 void testInputBuffer(const std::shared_ptr<android::Codec2Client::Component>& component,
69                      std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
70                      uint32_t flags, bool isNullBuffer) {
71     std::unique_ptr<C2Work> work;
72     {
73         typedef std::unique_lock<std::mutex> ULock;
74         ULock l(queueLock);
75         if (!workQueue.empty()) {
76             work.swap(workQueue.front());
77             workQueue.pop_front();
78         } else {
79             ASSERT_TRUE(false) << "workQueue Empty at the start of test";
80         }
81     }
82     ASSERT_NE(work, nullptr);
83 
84     work->input.flags = (C2FrameData::flags_t)flags;
85     work->input.ordinal.timestamp = 0;
86     work->input.ordinal.frameIndex = 0;
87     work->input.buffers.clear();
88     if (isNullBuffer) {
89         work->input.buffers.emplace_back(nullptr);
90     }
91     work->worklets.clear();
92     work->worklets.emplace_back(new C2Worklet);
93 
94     std::list<std::unique_ptr<C2Work>> items;
95     items.push_back(std::move(work));
96     ASSERT_EQ(component->queue(&items), C2_OK);
97 }
98 
99 // Wait for all the inputs to be consumed by the plugin.
waitOnInputConsumption(std::mutex & queueLock,std::condition_variable & queueCondition,std::list<std::unique_ptr<C2Work>> & workQueue,size_t bufferCount)100 void waitOnInputConsumption(std::mutex& queueLock, std::condition_variable& queueCondition,
101                             std::list<std::unique_ptr<C2Work>>& workQueue, size_t bufferCount) {
102     typedef std::unique_lock<std::mutex> ULock;
103     uint32_t queueSize;
104     uint32_t maxRetry = 0;
105     {
106         ULock l(queueLock);
107         queueSize = workQueue.size();
108     }
109     while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) {
110         ULock l(queueLock);
111         if (queueSize != workQueue.size()) {
112             queueSize = workQueue.size();
113             maxRetry = 0;
114         } else {
115             queueCondition.wait_for(l, TIME_OUT);
116             maxRetry++;
117         }
118     }
119 }
120 
121 // process onWorkDone received by Listener
workDone(const std::shared_ptr<android::Codec2Client::Component> & component,std::unique_ptr<C2Work> & work,std::list<uint64_t> & flushedIndices,std::mutex & queueLock,std::condition_variable & queueCondition,std::list<std::unique_ptr<C2Work>> & workQueue,bool & eos,bool & csd,uint32_t & framesReceived)122 void workDone(const std::shared_ptr<android::Codec2Client::Component>& component,
123               std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
124               std::mutex& queueLock, std::condition_variable& queueCondition,
125               std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
126               uint32_t& framesReceived) {
127     // handle configuration changes in work done
128     if (work->worklets.front()->output.configUpdate.size() != 0) {
129         ALOGV("Config Update");
130         std::vector<std::unique_ptr<C2Param>> updates =
131                 std::move(work->worklets.front()->output.configUpdate);
132         std::vector<C2Param*> configParam;
133         std::vector<std::unique_ptr<C2SettingResult>> failures;
134         for (size_t i = 0; i < updates.size(); ++i) {
135             C2Param* param = updates[i].get();
136             if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) {
137                 C2StreamInitDataInfo::output* csdBuffer = (C2StreamInitDataInfo::output*)(param);
138                 size_t csdSize = csdBuffer->flexCount();
139                 if (csdSize > 0) csd = true;
140             } else if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) ||
141                        (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE) ||
142                        (param->index() == C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
143                 configParam.push_back(param);
144             }
145         }
146         component->config(configParam, C2_DONT_BLOCK, &failures);
147         ASSERT_EQ(failures.size(), 0u);
148     }
149     if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
150         framesReceived++;
151         eos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
152         auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(),
153                                       work->input.ordinal.frameIndex.peeku());
154         ALOGV("WorkDone: frameID received %d",
155               (int)work->worklets.front()->output.ordinal.frameIndex.peeku());
156         work->input.buffers.clear();
157         work->worklets.clear();
158         {
159             typedef std::unique_lock<std::mutex> ULock;
160             ULock l(queueLock);
161             workQueue.push_back(std::move(work));
162             if (!flushedIndices.empty() && (frameIndexIt != flushedIndices.end())) {
163                 flushedIndices.erase(frameIndexIt);
164             }
165             queueCondition.notify_all();
166         }
167     }
168 }
169 
170 // Return current time in micro seconds
getNowUs()171 int64_t getNowUs() {
172     struct timeval tv;
173     gettimeofday(&tv, NULL);
174 
175     return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
176 }
177 
178 // Return all test parameters, a list of tuple of <instance, component>
getTestParameters()179 const std::vector<TestParameters>& getTestParameters() {
180     return getTestParameters(C2Component::DOMAIN_OTHER, C2Component::KIND_OTHER);
181 }
182 
183 // Return all test parameters, a list of tuple of <instance, component> with matching domain and
184 // kind.
getTestParameters(C2Component::domain_t domain,C2Component::kind_t kind)185 const std::vector<TestParameters>& getTestParameters(C2Component::domain_t domain,
186                                                      C2Component::kind_t kind) {
187     static std::vector<TestParameters> parameters;
188 
189     auto instances = android::Codec2Client::GetServiceNames();
190     for (std::string instance : instances) {
191         std::shared_ptr<android::Codec2Client> client =
192                 android::Codec2Client::CreateFromService(instance.c_str());
193         std::vector<C2Component::Traits> components = client->listComponents();
194         for (C2Component::Traits traits : components) {
195             if (instance.compare(traits.owner)) continue;
196             if (domain != C2Component::DOMAIN_OTHER &&
197                 (traits.domain != domain || traits.kind != kind)) {
198                 continue;
199             }
200             if (traits.name.rfind(sComponentNamePrefix, 0) != 0) {
201                 ALOGD("Skipping tests for %s. Prefix specified is %s", traits.name.c_str(),
202                       sComponentNamePrefix.c_str());
203                 continue;
204             }
205             parameters.push_back(std::make_tuple(instance, traits.name));
206         }
207     }
208 
209     if (parameters.empty()) {
210         ALOGE("No test parameters added. Verify component prefix passed to the test");
211     }
212     return parameters;
213 }
214 
215 // Populate Info vector and return number of CSDs
populateInfoVector(std::string info,android::Vector<FrameInfo> * frameInfo,bool timestampDevTest,std::list<uint64_t> * timestampUslist)216 int32_t populateInfoVector(std::string info, android::Vector<FrameInfo>* frameInfo,
217                            bool timestampDevTest, std::list<uint64_t>* timestampUslist) {
218     std::ifstream eleInfo;
219     eleInfo.open(info);
220     if (!eleInfo.is_open()) {
221         ALOGE("Can't open info file");
222         return -1;
223     }
224     int32_t numCsds = 0;
225     int32_t bytesCount = 0;
226     uint32_t flags = 0;
227     uint32_t timestamp = 0;
228     while (1) {
229         if (!(eleInfo >> bytesCount)) break;
230         eleInfo >> flags;
231         eleInfo >> timestamp;
232         bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
233         if (codecConfig) numCsds++;
234         bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
235         if (timestampDevTest && !codecConfig && !nonDisplayFrame) {
236             timestampUslist->push_back(timestamp);
237         }
238         frameInfo->push_back({bytesCount, flags, timestamp});
239     }
240     ALOGV("numCsds : %d", numCsds);
241     eleInfo.close();
242     return numCsds;
243 }
244 
verifyFlushOutput(std::list<std::unique_ptr<C2Work>> & flushedWork,std::list<std::unique_ptr<C2Work>> & workQueue,std::list<uint64_t> & flushedIndices,std::mutex & queueLock)245 void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
246                        std::list<std::unique_ptr<C2Work>>& workQueue,
247                        std::list<uint64_t>& flushedIndices, std::mutex& queueLock) {
248     // Update mFlushedIndices based on the index received from flush()
249     typedef std::unique_lock<std::mutex> ULock;
250     uint64_t frameIndex;
251     ULock l(queueLock);
252     for (std::unique_ptr<C2Work>& work : flushedWork) {
253         ASSERT_NE(work, nullptr);
254         frameIndex = work->input.ordinal.frameIndex.peeku();
255         std::list<uint64_t>::iterator frameIndexIt =
256                 std::find(flushedIndices.begin(), flushedIndices.end(), frameIndex);
257         if (!flushedIndices.empty() && (frameIndexIt != flushedIndices.end())) {
258             flushedIndices.erase(frameIndexIt);
259             work->input.buffers.clear();
260             work->worklets.clear();
261             workQueue.push_back(std::move(work));
262         }
263     }
264     ASSERT_EQ(flushedIndices.empty(), true);
265     flushedWork.clear();
266 }
267