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