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