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