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