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: 60 ::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 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 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. 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: 202 static void description(const std::string& description) { 203 RecordProperty("description", description); 204 } 205 }; 206 207 // Set Default port param. 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 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 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 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 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 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 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 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