• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
getTestCaseInfo() const60     ::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 
SetUp()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 
TearDown()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.
handleMessage(Message msg,const BufferInfo * buffer)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:
description(const std::string & description)202     static void description(const std::string& description) {
203         RecordProperty("description", description);
204     }
205 };
206 
207 // 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)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
GetURLForComponent(AudioEncHidlTest::standardComp comp,char * mURL)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
waitOnInputConsumption(sp<IOmxNode> omxNode,sp<CodecObserver> observer,android::Vector<BufferInfo> * iBuffer,android::Vector<BufferInfo> * oBuffer)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
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)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
TEST_F(AudioEncHidlTest,SetRole)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
TEST_F(AudioEncHidlTest,EnumeratePortFormat)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, &params);
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
TEST_F(AudioEncHidlTest,SimpleEncodeTest)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, &params);
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 
main(int argc,char ** argv)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