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 // 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)26 void testInputBuffer(const std::shared_ptr<android::Codec2Client::Component>& component,
27                      std::mutex& queueLock, std::list<std::unique_ptr<C2Work>>& workQueue,
28                      uint32_t flags, bool isNullBuffer) {
29     std::unique_ptr<C2Work> work;
30     {
31         typedef std::unique_lock<std::mutex> ULock;
32         ULock l(queueLock);
33         if (!workQueue.empty()) {
34             work.swap(workQueue.front());
35             workQueue.pop_front();
36         } else {
37             ASSERT_TRUE(false) << "workQueue Empty at the start of test";
38         }
39     }
40     ASSERT_NE(work, nullptr);
41 
42     work->input.flags = (C2FrameData::flags_t)flags;
43     work->input.ordinal.timestamp = 0;
44     work->input.ordinal.frameIndex = 0;
45     work->input.buffers.clear();
46     if (isNullBuffer) {
47         work->input.buffers.emplace_back(nullptr);
48     }
49     work->worklets.clear();
50     work->worklets.emplace_back(new C2Worklet);
51 
52     std::list<std::unique_ptr<C2Work>> items;
53     items.push_back(std::move(work));
54     ASSERT_EQ(component->queue(&items), C2_OK);
55 }
56 
57 // 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)58 void waitOnInputConsumption(std::mutex& queueLock, std::condition_variable& queueCondition,
59                             std::list<std::unique_ptr<C2Work>>& workQueue, size_t bufferCount) {
60     typedef std::unique_lock<std::mutex> ULock;
61     uint32_t queueSize;
62     uint32_t maxRetry = 0;
63     {
64         ULock l(queueLock);
65         queueSize = workQueue.size();
66     }
67     while ((maxRetry < MAX_RETRY) && (queueSize < bufferCount)) {
68         ULock l(queueLock);
69         if (queueSize != workQueue.size()) {
70             queueSize = workQueue.size();
71             maxRetry = 0;
72         } else {
73             queueCondition.wait_for(l, TIME_OUT);
74             maxRetry++;
75         }
76     }
77 }
78 
79 // 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)80 void workDone(const std::shared_ptr<android::Codec2Client::Component>& component,
81               std::unique_ptr<C2Work>& work, std::list<uint64_t>& flushedIndices,
82               std::mutex& queueLock, std::condition_variable& queueCondition,
83               std::list<std::unique_ptr<C2Work>>& workQueue, bool& eos, bool& csd,
84               uint32_t& framesReceived) {
85     // handle configuration changes in work done
86     if (work->worklets.front()->output.configUpdate.size() != 0) {
87         ALOGV("Config Update");
88         std::vector<std::unique_ptr<C2Param>> updates =
89                 std::move(work->worklets.front()->output.configUpdate);
90         std::vector<C2Param*> configParam;
91         std::vector<std::unique_ptr<C2SettingResult>> failures;
92         for (size_t i = 0; i < updates.size(); ++i) {
93             C2Param* param = updates[i].get();
94             if (param->index() == C2StreamInitDataInfo::output::PARAM_TYPE) {
95                 C2StreamInitDataInfo::output* csdBuffer =
96                         (C2StreamInitDataInfo::output*)(param);
97                 size_t csdSize = csdBuffer->flexCount();
98                 if (csdSize > 0) csd = true;
99             } else if ((param->index() == C2StreamSampleRateInfo::output::PARAM_TYPE) ||
100                        (param->index() == C2StreamChannelCountInfo::output::PARAM_TYPE) ||
101                        (param->index() == C2StreamPictureSizeInfo::output::PARAM_TYPE)) {
102                 configParam.push_back(param);
103             }
104         }
105         component->config(configParam, C2_DONT_BLOCK, &failures);
106         ASSERT_EQ(failures.size(), 0u);
107     }
108     if (work->worklets.front()->output.flags != C2FrameData::FLAG_INCOMPLETE) {
109         framesReceived++;
110         eos = (work->worklets.front()->output.flags & C2FrameData::FLAG_END_OF_STREAM) != 0;
111         auto frameIndexIt = std::find(flushedIndices.begin(), flushedIndices.end(),
112                                       work->input.ordinal.frameIndex.peeku());
113         ALOGV("WorkDone: frameID received %d",
114               (int)work->worklets.front()->output.ordinal.frameIndex.peeku());
115         work->input.buffers.clear();
116         work->worklets.clear();
117         {
118             typedef std::unique_lock<std::mutex> ULock;
119             ULock l(queueLock);
120             workQueue.push_back(std::move(work));
121             if (!flushedIndices.empty() &&
122                 (frameIndexIt != flushedIndices.end())) {
123                 flushedIndices.erase(frameIndexIt);
124             }
125             queueCondition.notify_all();
126         }
127     }
128 }
129 
130 // Return current time in micro seconds
getNowUs()131 int64_t getNowUs() {
132     struct timeval tv;
133     gettimeofday(&tv, NULL);
134 
135     return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
136 }
137 
138 // Return all test parameters, a list of tuple of <instance, component>
getTestParameters()139 const std::vector<std::tuple<std::string, std::string>>& getTestParameters() {
140     return getTestParameters(C2Component::DOMAIN_OTHER, C2Component::KIND_OTHER);
141 }
142 
143 // Return all test parameters, a list of tuple of <instance, component> with matching domain and
144 // kind.
getTestParameters(C2Component::domain_t domain,C2Component::kind_t kind)145 const std::vector<std::tuple<std::string, std::string>>& getTestParameters(
146         C2Component::domain_t domain, C2Component::kind_t kind) {
147     static std::vector<std::tuple<std::string, std::string>> parameters;
148 
149     auto instances = android::Codec2Client::GetServiceNames();
150     for (std::string instance : instances) {
151         std::shared_ptr<android::Codec2Client> client =
152                 android::Codec2Client::CreateFromService(instance.c_str());
153         std::vector<C2Component::Traits> components = client->listComponents();
154         for (C2Component::Traits traits : components) {
155             if (instance.compare(traits.owner)) continue;
156             if (domain != C2Component::DOMAIN_OTHER &&
157                 (traits.domain != domain || traits.kind != kind)) {
158                 continue;
159             }
160 
161             parameters.push_back(std::make_tuple(instance, traits.name));
162         }
163     }
164 
165     return parameters;
166 }
167 
168 // Populate Info vector and return number of CSDs
populateInfoVector(std::string info,android::Vector<FrameInfo> * frameInfo,bool timestampDevTest,std::list<uint64_t> * timestampUslist)169 int32_t populateInfoVector(std::string info, android::Vector<FrameInfo>* frameInfo,
170                            bool timestampDevTest, std::list<uint64_t>* timestampUslist) {
171     std::ifstream eleInfo;
172     eleInfo.open(info);
173     if (!eleInfo.is_open()) {
174         ALOGE("Can't open info file");
175         return -1;
176     }
177     int32_t numCsds = 0;
178     int32_t bytesCount = 0;
179     uint32_t flags = 0;
180     uint32_t timestamp = 0;
181     while (1) {
182         if (!(eleInfo >> bytesCount)) break;
183         eleInfo >> flags;
184         eleInfo >> timestamp;
185         bool codecConfig = flags ? ((1 << (flags - 1)) & C2FrameData::FLAG_CODEC_CONFIG) != 0 : 0;
186         if (codecConfig) numCsds++;
187         bool nonDisplayFrame = ((flags & FLAG_NON_DISPLAY_FRAME) != 0);
188         if (timestampDevTest && !codecConfig && !nonDisplayFrame) {
189             timestampUslist->push_back(timestamp);
190         }
191         frameInfo->push_back({bytesCount, flags, timestamp});
192     }
193     ALOGV("numCsds : %d", numCsds);
194     eleInfo.close();
195     return numCsds;
196 }
197 
verifyFlushOutput(std::list<std::unique_ptr<C2Work>> & flushedWork,std::list<std::unique_ptr<C2Work>> & workQueue,std::list<uint64_t> & flushedIndices,std::mutex & queueLock)198 void verifyFlushOutput(std::list<std::unique_ptr<C2Work>>& flushedWork,
199                        std::list<std::unique_ptr<C2Work>>& workQueue,
200                        std::list<uint64_t>& flushedIndices, std::mutex& queueLock) {
201     // Update mFlushedIndices based on the index received from flush()
202     typedef std::unique_lock<std::mutex> ULock;
203     uint64_t frameIndex;
204     ULock l(queueLock);
205     for (std::unique_ptr<C2Work>& work : flushedWork) {
206         ASSERT_NE(work, nullptr);
207         frameIndex = work->input.ordinal.frameIndex.peeku();
208         std::list<uint64_t>::iterator frameIndexIt =
209                 std::find(flushedIndices.begin(), flushedIndices.end(), frameIndex);
210         if (!flushedIndices.empty() && (frameIndexIt != flushedIndices.end())) {
211             flushedIndices.erase(frameIndexIt);
212             work->input.buffers.clear();
213             work->worklets.clear();
214             workQueue.push_back(std::move(work));
215         }
216     }
217     ASSERT_EQ(flushedIndices.empty(), true);
218     flushedWork.clear();
219 }
220