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