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, &params);
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, &params);
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 }