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