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