xref: /aosp_15_r20/frameworks/av/media/codec2/hal/hidl/1.0/vts/functional/common/media_c2_hidl_test_common.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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