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