1 /*
2 * Copyright (C) 2017 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_TAG "media_omx_hidl_audio_enc_test"
18 #ifdef __LP64__
19 #define OMX_ANDROID_COMPILE_AS_32BIT_ON_64BIT_PLATFORMS
20 #endif
21
22 #include <android-base/logging.h>
23
24 #include <android/hardware/media/omx/1.0/IOmx.h>
25 #include <android/hardware/media/omx/1.0/IOmxNode.h>
26 #include <android/hardware/media/omx/1.0/IOmxObserver.h>
27 #include <android/hardware/media/omx/1.0/types.h>
28 #include <android/hidl/allocator/1.0/IAllocator.h>
29 #include <android/hidl/memory/1.0/IMapper.h>
30 #include <android/hidl/memory/1.0/IMemory.h>
31
32 using ::android::hardware::media::omx::V1_0::IOmx;
33 using ::android::hardware::media::omx::V1_0::IOmxObserver;
34 using ::android::hardware::media::omx::V1_0::IOmxNode;
35 using ::android::hardware::media::omx::V1_0::Message;
36 using ::android::hardware::media::omx::V1_0::CodecBuffer;
37 using ::android::hardware::media::omx::V1_0::PortMode;
38 using ::android::hidl::allocator::V1_0::IAllocator;
39 using ::android::hidl::memory::V1_0::IMemory;
40 using ::android::hidl::memory::V1_0::IMapper;
41 using ::android::hardware::Return;
42 using ::android::hardware::Void;
43 using ::android::hardware::hidl_vec;
44 using ::android::hardware::hidl_string;
45 using ::android::sp;
46
47 #include <VtsHalHidlTargetTestBase.h>
48 #include <getopt.h>
49 #include <media_audio_hidl_test_common.h>
50 #include <media_hidl_test_common.h>
51 #include <fstream>
52
53 static ComponentTestEnvironment* gEnv = nullptr;
54
55 // audio encoder test fixture class
56 class AudioEncHidlTest : public ::testing::VtsHalHidlTargetTestBase {
57 private:
58 typedef ::testing::VtsHalHidlTargetTestBase Super;
59 public:
getTestCaseInfo() const60 ::std::string getTestCaseInfo() const override {
61 return ::std::string() +
62 "Component: " + gEnv->getComponent().c_str() + " | " +
63 "Role: " + gEnv->getRole().c_str() + " | " +
64 "Instance: " + gEnv->getInstance().c_str() + " | " +
65 "Res: " + gEnv->getRes().c_str();
66 }
67
SetUp()68 virtual void SetUp() override {
69 Super::SetUp();
70 disableTest = false;
71 android::hardware::media::omx::V1_0::Status status;
72 omx = Super::getService<IOmx>(gEnv->getInstance());
73 ASSERT_NE(omx, nullptr);
74 observer =
75 new CodecObserver([this](Message msg, const BufferInfo* buffer) {
76 handleMessage(msg, buffer);
77 });
78 ASSERT_NE(observer, nullptr);
79 if (strncmp(gEnv->getComponent().c_str(), "OMX.", 4) != 0)
80 disableTest = true;
81 EXPECT_TRUE(omx->allocateNode(
82 gEnv->getComponent(), observer,
83 [&](android::hardware::media::omx::V1_0::Status _s,
84 sp<IOmxNode> const& _nl) {
85 status = _s;
86 this->omxNode = _nl;
87 })
88 .isOk());
89 if (status == android::hardware::media::omx::V1_0::Status::NAME_NOT_FOUND) {
90 disableTest = true;
91 std::cout << "[ WARN ] Test Disabled, component not present\n";
92 return;
93 }
94 ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
95 ASSERT_NE(omxNode, nullptr);
96 ASSERT_NE(gEnv->getRole().empty(), true) << "Invalid Component Role";
97 struct StringToName {
98 const char* Name;
99 standardComp CompName;
100 };
101 const StringToName kStringToName[] = {
102 {"amrnb", amrnb}, {"amrwb", amrwb}, {"aac", aac}, {"flac", flac},
103 };
104 const size_t kNumStringToName =
105 sizeof(kStringToName) / sizeof(kStringToName[0]);
106 const char* pch;
107 char substring[OMX_MAX_STRINGNAME_SIZE];
108 strcpy(substring, gEnv->getRole().c_str());
109 pch = strchr(substring, '.');
110 ASSERT_NE(pch, nullptr);
111 compName = unknown_comp;
112 for (size_t i = 0; i < kNumStringToName; ++i) {
113 if (!strcasecmp(pch + 1, kStringToName[i].Name)) {
114 compName = kStringToName[i].CompName;
115 break;
116 }
117 }
118 if (compName == unknown_comp) disableTest = true;
119 struct CompToCoding {
120 standardComp CompName;
121 OMX_AUDIO_CODINGTYPE eEncoding;
122 };
123 static const CompToCoding kCompToCoding[] = {
124 {amrnb, OMX_AUDIO_CodingAMR},
125 {amrwb, OMX_AUDIO_CodingAMR},
126 {aac, OMX_AUDIO_CodingAAC},
127 {flac, OMX_AUDIO_CodingFLAC},
128 };
129 static const size_t kNumCompToCoding =
130 sizeof(kCompToCoding) / sizeof(kCompToCoding[0]);
131 size_t i;
132 for (i = 0; i < kNumCompToCoding; ++i) {
133 if (kCompToCoding[i].CompName == compName) {
134 eEncoding = kCompToCoding[i].eEncoding;
135 break;
136 }
137 }
138 if (i == kNumCompToCoding) disableTest = true;
139 eosFlag = false;
140 if (disableTest) std::cout << "[ WARN ] Test Disabled \n";
141 }
142
TearDown()143 virtual void TearDown() override {
144 if (omxNode != nullptr) {
145 // If you have encountered a fatal failure, it is possible that
146 // freeNode() will not go through. Instead of hanging the app.
147 // let it pass through and report errors
148 if (::testing::Test::HasFatalFailure()) return;
149 EXPECT_TRUE((omxNode->freeNode()).isOk());
150 omxNode = nullptr;
151 }
152 Super::TearDown();
153 }
154
155 // callback function to process messages received by onMessages() from IL
156 // client.
handleMessage(Message msg,const BufferInfo * buffer)157 void handleMessage(Message msg, const BufferInfo* buffer) {
158 (void)buffer;
159
160 if (msg.type == Message::Type::FILL_BUFFER_DONE) {
161 if (msg.data.extendedBufferData.flags & OMX_BUFFERFLAG_EOS) {
162 eosFlag = true;
163 }
164 if (msg.data.extendedBufferData.rangeLength != 0) {
165 #define WRITE_OUTPUT 0
166 #if WRITE_OUTPUT
167 static int count = 0;
168 FILE* ofp = nullptr;
169 if (count)
170 ofp = fopen("out.bin", "ab");
171 else
172 ofp = fopen("out.bin", "wb");
173 if (ofp != nullptr) {
174 fwrite(static_cast<void*>(buffer->mMemory->getPointer()),
175 sizeof(char),
176 msg.data.extendedBufferData.rangeLength, ofp);
177 fclose(ofp);
178 count++;
179 }
180 #endif
181 }
182 }
183 }
184
185 enum standardComp {
186 amrnb,
187 amrwb,
188 aac,
189 flac,
190 unknown_comp,
191 };
192
193 sp<IOmx> omx;
194 sp<CodecObserver> observer;
195 sp<IOmxNode> omxNode;
196 standardComp compName;
197 OMX_AUDIO_CODINGTYPE eEncoding;
198 bool disableTest;
199 bool eosFlag;
200
201 protected:
description(const std::string & description)202 static void description(const std::string& description) {
203 RecordProperty("description", description);
204 }
205 };
206
207 // Set Default port param.
setDefaultPortParam(sp<IOmxNode> omxNode,OMX_U32 portIndex,OMX_AUDIO_CODINGTYPE eEncoding,AudioEncHidlTest::standardComp comp,int32_t nChannels,int32_t nSampleRate,int32_t nBitRate)208 void setDefaultPortParam(sp<IOmxNode> omxNode, OMX_U32 portIndex,
209 OMX_AUDIO_CODINGTYPE eEncoding,
210 AudioEncHidlTest::standardComp comp, int32_t nChannels,
211 int32_t nSampleRate, int32_t nBitRate) {
212 android::hardware::media::omx::V1_0::Status status;
213
214 OMX_PARAM_PORTDEFINITIONTYPE portDef;
215 status = getPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
216 &portDef);
217 EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
218
219 portDef.format.audio.bFlagErrorConcealment = OMX_TRUE;
220 portDef.format.audio.eEncoding = eEncoding;
221 status = setPortParam(omxNode, OMX_IndexParamPortDefinition, portIndex,
222 &portDef);
223 EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
224
225 std::vector<int32_t> arrProfile;
226 int32_t profile;
227 if ((int)eEncoding == OMX_AUDIO_CodingAAC) {
228 enumerateProfile(omxNode, portIndex, &arrProfile);
229 if (arrProfile.empty() == true) ASSERT_TRUE(false);
230 profile = arrProfile[0];
231 }
232
233 switch ((int)eEncoding) {
234 case OMX_AUDIO_CodingFLAC:
235 setupFLACPort(omxNode, portIndex, nChannels, nSampleRate,
236 5 /* nCompressionLevel */);
237 break;
238 case OMX_AUDIO_CodingAMR:
239 setupAMRPort(omxNode, portIndex, nBitRate,
240 (comp == AudioEncHidlTest::standardComp::amrwb));
241 break;
242 case OMX_AUDIO_CodingAAC:
243 setupAACPort(omxNode, portIndex,
244 static_cast<OMX_AUDIO_AACPROFILETYPE>(profile),
245 OMX_AUDIO_AACStreamFormatMP4FF, nChannels, nBitRate,
246 nSampleRate);
247 break;
248 default:
249 break;
250 }
251 }
252
253 // LookUpTable of clips and metadata for component testing
GetURLForComponent(AudioEncHidlTest::standardComp comp,char * mURL)254 void GetURLForComponent(AudioEncHidlTest::standardComp comp, char* mURL) {
255 struct CompToURL {
256 AudioEncHidlTest::standardComp comp;
257 const char* mURL;
258 };
259 static const CompToURL kCompToURL[] = {
260 {AudioEncHidlTest::standardComp::aac, "bbb_raw_2ch_48khz_s16le.raw"},
261 {AudioEncHidlTest::standardComp::amrnb, "bbb_raw_1ch_8khz_s16le.raw"},
262 {AudioEncHidlTest::standardComp::amrwb, "bbb_raw_1ch_16khz_s16le.raw"},
263 {AudioEncHidlTest::standardComp::flac, "bbb_raw_2ch_48khz_s16le.raw"},
264 };
265
266 for (size_t i = 0; i < sizeof(kCompToURL) / sizeof(kCompToURL[0]); ++i) {
267 if (kCompToURL[i].comp == comp) {
268 strcat(mURL, kCompToURL[i].mURL);
269 return;
270 }
271 }
272 }
273
274 // blocking call to ensures application to Wait till all the inputs are consumed
waitOnInputConsumption(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer)275 void waitOnInputConsumption(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
276 android::Vector<BufferInfo>* iBuffer,
277 android::Vector<BufferInfo>* oBuffer) {
278 android::hardware::media::omx::V1_0::Status status;
279 Message msg;
280 int timeOut = TIMEOUT_COUNTER_Q;
281
282 while (timeOut--) {
283 size_t i = 0;
284 status =
285 observer->dequeueMessage(&msg, DEFAULT_TIMEOUT_Q, iBuffer, oBuffer);
286 ASSERT_EQ(status,
287 android::hardware::media::omx::V1_0::Status::TIMED_OUT);
288 // status == TIMED_OUT, it could be due to process time being large
289 // than DEFAULT_TIMEOUT or component needs output buffers to start
290 // processing.
291 for (; i < iBuffer->size(); i++) {
292 if ((*iBuffer)[i].owner != client) break;
293 }
294 if (i == iBuffer->size()) break;
295
296 // Dispatch an output buffer assuming outQueue.empty() is true
297 size_t index;
298 if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) {
299 ASSERT_NO_FATAL_FAILURE(
300 dispatchOutputBuffer(omxNode, oBuffer, index));
301 timeOut = TIMEOUT_COUNTER_Q;
302 }
303 }
304 }
305
306 // Encode N Frames
encodeNFrames(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer,uint32_t nFrames,int32_t samplesPerFrame,int32_t nChannels,int32_t nSampleRate,std::ifstream & eleStream,bool signalEOS=true)307 void encodeNFrames(sp<IOmxNode> omxNode, sp<CodecObserver> observer,
308 android::Vector<BufferInfo>* iBuffer,
309 android::Vector<BufferInfo>* oBuffer, uint32_t nFrames,
310 int32_t samplesPerFrame, int32_t nChannels,
311 int32_t nSampleRate, std::ifstream& eleStream,
312 bool signalEOS = true) {
313 android::hardware::media::omx::V1_0::Status status;
314 Message msg;
315 size_t index;
316 int bytesCount = samplesPerFrame * nChannels * 2;
317 int32_t timestampIncr =
318 (int)(((float)samplesPerFrame / nSampleRate) * 1000000);
319 uint64_t timestamp = 0;
320 uint32_t flags = 0;
321 int timeOut = TIMEOUT_COUNTER_Q;
322 bool iQueued, oQueued;
323
324 while (1) {
325 iQueued = oQueued = false;
326 status =
327 observer->dequeueMessage(&msg, DEFAULT_TIMEOUT_Q, iBuffer, oBuffer);
328 if (status == android::hardware::media::omx::V1_0::Status::OK)
329 ASSERT_TRUE(false);
330
331 if (nFrames == 0) break;
332
333 // Dispatch input buffer
334 if ((index = getEmptyBufferID(iBuffer)) < iBuffer->size()) {
335 char* ipBuffer = static_cast<char*>(
336 static_cast<void*>((*iBuffer)[index].mMemory->getPointer()));
337 ASSERT_LE(bytesCount,
338 static_cast<int>((*iBuffer)[index].mMemory->getSize()));
339 eleStream.read(ipBuffer, bytesCount);
340 if (eleStream.gcount() != bytesCount) break;
341 flags = OMX_BUFFERFLAG_ENDOFFRAME;
342 if (signalEOS && (nFrames == 1)) flags |= OMX_BUFFERFLAG_EOS;
343 ASSERT_NO_FATAL_FAILURE(dispatchInputBuffer(
344 omxNode, iBuffer, index, bytesCount, flags, timestamp));
345 timestamp += timestampIncr;
346 nFrames--;
347 iQueued = true;
348 }
349 // Dispatch output buffer
350 if ((index = getEmptyBufferID(oBuffer)) < oBuffer->size()) {
351 ASSERT_NO_FATAL_FAILURE(
352 dispatchOutputBuffer(omxNode, oBuffer, index));
353 oQueued = true;
354 }
355 // Reset Counters when either input or output buffer is dispatched
356 if (iQueued || oQueued)
357 timeOut = TIMEOUT_COUNTER_Q;
358 else
359 timeOut--;
360 if (timeOut == 0) {
361 ASSERT_TRUE(false) << "Wait on Input/Output is found indefinite";
362 }
363 }
364 }
365
366 // set component role
TEST_F(AudioEncHidlTest,SetRole)367 TEST_F(AudioEncHidlTest, SetRole) {
368 description("Test Set Component Role");
369 if (disableTest) return;
370 android::hardware::media::omx::V1_0::Status status;
371 status = setRole(omxNode, gEnv->getRole().c_str());
372 ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
373 }
374
375 // port format enumeration
TEST_F(AudioEncHidlTest,EnumeratePortFormat)376 TEST_F(AudioEncHidlTest, EnumeratePortFormat) {
377 description("Test Component on Mandatory Port Parameters (Port Format)");
378 if (disableTest) return;
379 android::hardware::media::omx::V1_0::Status status;
380 uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
381 status = setRole(omxNode, gEnv->getRole().c_str());
382 ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
383 OMX_PORT_PARAM_TYPE params;
384 status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms);
385 if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
386 ASSERT_EQ(params.nPorts, 2U);
387 kPortIndexInput = params.nStartPortNumber;
388 kPortIndexOutput = kPortIndexInput + 1;
389 }
390 status = setAudioPortFormat(omxNode, kPortIndexInput, OMX_AUDIO_CodingPCM);
391 EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
392 status = setAudioPortFormat(omxNode, kPortIndexOutput, eEncoding);
393 EXPECT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
394 }
395
396 // test raw stream encode
TEST_F(AudioEncHidlTest,SimpleEncodeTest)397 TEST_F(AudioEncHidlTest, SimpleEncodeTest) {
398 description("Tests Basic encoding and EOS");
399 if (disableTest) return;
400 android::hardware::media::omx::V1_0::Status status;
401 uint32_t kPortIndexInput = 0, kPortIndexOutput = 1;
402 status = setRole(omxNode, gEnv->getRole().c_str());
403 ASSERT_EQ(status, ::android::hardware::media::omx::V1_0::Status::OK);
404 OMX_PORT_PARAM_TYPE params;
405 status = getParam(omxNode, OMX_IndexParamAudioInit, ¶ms);
406 if (status == ::android::hardware::media::omx::V1_0::Status::OK) {
407 ASSERT_EQ(params.nPorts, 2U);
408 kPortIndexInput = params.nStartPortNumber;
409 kPortIndexOutput = kPortIndexInput + 1;
410 }
411 char mURL[512];
412 strcpy(mURL, gEnv->getRes().c_str());
413 GetURLForComponent(compName, mURL);
414
415 std::ifstream eleStream;
416
417 // Configure input port
418 int32_t nChannels = 2;
419 int32_t nSampleRate = 44100;
420 int32_t samplesPerFrame = 1024;
421 int32_t nBitRate = 128000;
422 switch (compName) {
423 case amrnb:
424 nChannels = 1;
425 nSampleRate = 8000;
426 samplesPerFrame = 160;
427 nBitRate = 7400;
428 break;
429 case amrwb:
430 nChannels = 1;
431 nSampleRate = 16000;
432 samplesPerFrame = 160;
433 nBitRate = 15850;
434 break;
435 case aac:
436 nChannels = 2;
437 nSampleRate = 48000;
438 samplesPerFrame = 1024;
439 nBitRate = 128000;
440 break;
441 case flac:
442 nChannels = 2;
443 nSampleRate = 48000;
444 samplesPerFrame = 1152;
445 nBitRate = 128000;
446 break;
447 default:
448 ASSERT_TRUE(false);
449 }
450 setupPCMPort(omxNode, kPortIndexInput, nChannels, OMX_NumericalDataSigned,
451 16, nSampleRate, OMX_AUDIO_PCMModeLinear);
452
453 // Configure output port
454 ASSERT_NO_FATAL_FAILURE(setDefaultPortParam(omxNode, kPortIndexOutput,
455 eEncoding, compName, nChannels,
456 nSampleRate, nBitRate));
457
458 android::Vector<BufferInfo> iBuffer, oBuffer;
459
460 // set state to idle
461 ASSERT_NO_FATAL_FAILURE(changeStateLoadedtoIdle(omxNode, observer, &iBuffer,
462 &oBuffer, kPortIndexInput,
463 kPortIndexOutput));
464 // set state to executing
465 ASSERT_NO_FATAL_FAILURE(changeStateIdletoExecute(omxNode, observer));
466
467 eleStream.open(mURL, std::ifstream::binary);
468 ASSERT_EQ(eleStream.is_open(), true);
469 ASSERT_NO_FATAL_FAILURE(encodeNFrames(omxNode, observer, &iBuffer, &oBuffer,
470 128, samplesPerFrame, nChannels,
471 nSampleRate, eleStream));
472 eleStream.close();
473
474 ASSERT_NO_FATAL_FAILURE(
475 waitOnInputConsumption(omxNode, observer, &iBuffer, &oBuffer));
476 ASSERT_NO_FATAL_FAILURE(
477 testEOS(omxNode, observer, &iBuffer, &oBuffer, false, eosFlag));
478 // set state to idle
479 ASSERT_NO_FATAL_FAILURE(
480 changeStateExecutetoIdle(omxNode, observer, &iBuffer, &oBuffer));
481 // set state to executing
482 ASSERT_NO_FATAL_FAILURE(changeStateIdletoLoaded(omxNode, observer, &iBuffer,
483 &oBuffer, kPortIndexInput,
484 kPortIndexOutput));
485 }
486
main(int argc,char ** argv)487 int main(int argc, char** argv) {
488 gEnv = new ComponentTestEnvironment();
489 ::testing::AddGlobalTestEnvironment(gEnv);
490 ::testing::InitGoogleTest(&argc, argv);
491 gEnv->init(&argc, argv);
492 int status = gEnv->initFromOptions(argc, argv);
493 if (status == 0) {
494 status = RUN_ALL_TESTS();
495 ALOGI("Test result = %d", status);
496 }
497 return status;
498 }
499