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