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