Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 5c48bcb6 authored by Neelkamal Semwal's avatar Neelkamal Semwal
Browse files

MediaTesting: Add HEVC Utility Unit Test

Test: atest HEVCUtilsUnitTest -- --enable-module-dynamic-download=true

Bug: 152366808

Change-Id: I77a19e5ecb56b9e3cdcbd682f4bf77a608b25a3e
parent 27c20f48
Loading
Loading
Loading
Loading
+51 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

cc_test {
    name: "HEVCUtilsUnitTest",
    gtest: true,

    srcs: [
        "HEVCUtilsUnitTest.cpp",
    ],

    shared_libs: [
        "libutils",
        "liblog",
    ],

    static_libs: [
        "libstagefright",
        "libstagefright_foundation",
    ],

    include_dirs: [
        "frameworks/av/media/libstagefright",
    ],

    cflags: [
        "-Werror",
        "-Wall",
    ],

    sanitize: {
        cfi: true,
        misc_undefined: [
            "unsigned-integer-overflow",
            "signed-integer-overflow",
        ],
    },
}
+31 −0
Original line number Diff line number Diff line
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2020 The Android Open Source Project

     Licensed under the Apache License, Version 2.0 (the "License");
     you may not use this file except in compliance with the License.
     You may obtain a copy of the License at

          http://www.apache.org/licenses/LICENSE-2.0

     Unless required by applicable law or agreed to in writing, software
     distributed under the License is distributed on an "AS IS" BASIS,
     WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     See the License for the specific language governing permissions and
     limitations under the License.
-->
<configuration description="Test module config for HEVC Utils unit tests">
    <option name="test-suite-tag" value="HEVCUtilsUnitTest" />
    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
        <option name="cleanup" value="false" />
        <option name="push" value="HEVCUtilsUnitTest->/data/local/tmp/HEVCUtilsUnitTest" />
        <option name="push-file"
            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/HEVCUtils/HEVCUtilsUnitTest.zip?unzip=true"
            value="/data/local/tmp/HEVCUtilsUnitTest/" />
    </target_preparer>

    <test class="com.android.tradefed.testtype.GTest" >
        <option name="native-test-device-path" value="/data/local/tmp" />
        <option name="module-name" value="HEVCUtilsUnitTest" />
        <option name="native-test-flag" value="-P /data/local/tmp/HEVCUtilsUnitTest/" />
    </test>
</configuration>
+73 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

#ifndef __HEVC_UTILS_TEST_ENVIRONMENT_H__
#define __HEVC_UTILS_TEST_ENVIRONMENT_H__

#include <gtest/gtest.h>

#include <getopt.h>

using namespace std;

class HEVCUtilsTestEnvironment : public::testing::Environment {
  public:
    HEVCUtilsTestEnvironment() : res("/data/local/tmp/") {}

    // Parses the command line arguments
    int initFromOptions(int argc, char **argv);

    void setRes(const char *_res) { res = _res; }

    const string getRes() const { return res; }

  private:
    string res;
};

int HEVCUtilsTestEnvironment::initFromOptions(int argc, char **argv) {
    static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};

    while (true) {
        int index = 0;
        int c = getopt_long(argc, argv, "P:", options, &index);
        if (c == -1) {
            break;
        }

        switch (c) {
            case 'P': {
                setRes(optarg);
                break;
            }
            default:
                break;
        }
    }

    if (optind < argc) {
        fprintf(stderr,
                "unrecognized option: %s\n\n"
                "usage: %s <gtest options> <test options>\n\n"
                "test options are:\n\n"
                "-P, --path: Resource files directory location\n",
                argv[optind ?: 1], argv[0]);
        return 2;
    }
    return 0;
}

#endif  // __HEVC_UTILS_TEST_ENVIRONMENT_H__
+208 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2020 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

//#define LOG_NDEBUG 0
#define LOG_TAG "HevcUtilityTest"
#include <utils/Log.h>

#include <fstream>

#include <media/stagefright/foundation/ABitReader.h>
#include "include/HevcUtils.h"

#include "HEVCUtilsTestEnvironment.h"

using namespace android;

// max size of hvcc box is 2 KB
constexpr uint32_t kHvccBoxMaxSize = 2048;
constexpr uint32_t kHvccBoxMinSize = 20;
constexpr uint32_t kVPSCode = 32;
constexpr uint32_t kSPSCode = 33;
constexpr uint32_t kPPSCode = 34;
constexpr uint32_t kNALSizeLength = 2;

static HEVCUtilsTestEnvironment *gEnv = nullptr;

class HEVCUtilsUnitTest
    : public ::testing::TestWithParam<
              tuple</*fileName*/ string, /*infoFileName*/ string, /*numVPSNals*/ size_t,
                    /*numSPSNals*/ size_t, /*numPPSNals*/ size_t, /*frameRate*/ int16_t,
                    /*isHdr*/ bool>> {
  public:
    ~HEVCUtilsUnitTest() {
        if (mMediaFileStream.is_open()) mMediaFileStream.close();
        if (mInfoFileStream.is_open()) mInfoFileStream.close();
    }

    virtual void SetUp() override {
        tuple<string, string, size_t, size_t, size_t, int16_t, bool> params = GetParam();
        string inputMediaFile = gEnv->getRes() + get<0>(params);
        mMediaFileStream.open(inputMediaFile, ifstream::in);
        ASSERT_TRUE(mMediaFileStream.is_open()) << "Failed to open media file: " << inputMediaFile;

        string inputInfoFile = gEnv->getRes() + get<1>(params);
        mInfoFileStream.open(inputInfoFile, ifstream::in);
        ASSERT_TRUE(mInfoFileStream.is_open()) << "Failed to open info file: " << inputInfoFile;

        mNumVPSNals = get<2>(params);
        mNumSPSNals = get<3>(params);
        mNumPPSNals = get<4>(params);
        mFrameRate = get<5>(params);
        mIsHDR = get<6>(params);
    }

    size_t mNumVPSNals;
    size_t mNumSPSNals;
    size_t mNumPPSNals;
    int16_t mFrameRate;
    bool mIsHDR;
    ifstream mMediaFileStream;
    ifstream mInfoFileStream;
};

TEST_P(HEVCUtilsUnitTest, NALUnitTest) {
    HevcParameterSets hevcParams;

    string line;
    int32_t index = 0;
    status_t err;
    while (getline(mInfoFileStream, line)) {
        string type;
        int32_t chunkLength;

        istringstream stringLine(line);
        stringLine >> type >> chunkLength;
        ASSERT_GT(chunkLength, 0) << "Length of data chunk must be greater than 0";

        char *data = (char *)malloc(chunkLength);
        ASSERT_NE(data, nullptr) << "Failed to allocate data buffer of size: " << chunkLength;

        mMediaFileStream.read(data, chunkLength);
        ASSERT_EQ(mMediaFileStream.gcount(), chunkLength)
                << "Failed to read complete file, bytes read: " << mMediaFileStream.gcount();

        // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
        int32_t offset = 0;
        for (; offset + 2 < chunkLength; ++offset) {
            if (data[offset + 2] == 0x01 && data[offset + 1] == 0x00 && data[offset] == 0x00) {
                break;
            }
        }
        offset += 3;
        ASSERT_LE(offset, chunkLength) << "NAL unit offset must not exceed the chunk length";

        uint8_t *nalUnit = (uint8_t *)(data + offset);
        size_t nalUnitLength = chunkLength - offset;

        // Add NAL units only if they're of type: VPS/SPS/PPS/SEI
        if (!((type.compare("VPS") && type.compare("SPS") && type.compare("PPS") &&
               type.compare("SEI")))) {
            err = hevcParams.addNalUnit(nalUnit, nalUnitLength);
            ASSERT_EQ(err, (status_t)OK)
                    << "Failed to add NAL Unit type: " << type << " Size: " << nalUnitLength;

            size_t sizeNalUnit = hevcParams.getSize(index);
            ASSERT_EQ(sizeNalUnit, nalUnitLength) << "Invalid size returned for NAL: " << type;

            uint8_t *destination = (uint8_t *)malloc(nalUnitLength);
            ASSERT_NE(destination, nullptr)
                    << "Failed to allocate buffer of size: " << nalUnitLength;

            bool status = hevcParams.write(index, destination, nalUnitLength);
            ASSERT_TRUE(status) << "Unable to write NAL Unit data";

            free(destination);
            index++;
        } else {
            err = hevcParams.addNalUnit(nalUnit, nalUnitLength);
            ASSERT_NE(err, (status_t)OK) << "Invalid NAL Unit added, type: " << type;
        }
        free(data);
    }

    size_t numNalUnits = hevcParams.getNumNalUnitsOfType(kVPSCode);
    ASSERT_EQ(numNalUnits, mNumVPSNals) << "Wrong number of VPS NAL Units";

    numNalUnits = hevcParams.getNumNalUnitsOfType(kSPSCode);
    ASSERT_EQ(numNalUnits, mNumSPSNals) << "Wrong number of SPS NAL Units";

    numNalUnits = hevcParams.getNumNalUnitsOfType(kPPSCode);
    ASSERT_EQ(numNalUnits, mNumPPSNals) << "Wrong number of PPS NAL Units";

    HevcParameterSets::Info info = hevcParams.getInfo();
    ASSERT_EQ(info & HevcParameterSets::kInfoIsHdr,
              (mIsHDR ? HevcParameterSets::kInfoIsHdr : HevcParameterSets::kInfoNone))
            << "Wrong info about HDR";

    ASSERT_EQ(info & HevcParameterSets::kInfoHasColorDescription,
              (mIsHDR ? HevcParameterSets::kInfoHasColorDescription : HevcParameterSets::kInfoNone))
            << "Wrong info about color description";

    // an HEVC file starts with VPS, SPS and PPS NAL units in sequence.
    uint8_t typeNalUnit = hevcParams.getType(0);
    ASSERT_EQ(typeNalUnit, kHevcNalUnitTypeVps)
            << "Expected NAL type: 32(VPS), found: " << typeNalUnit;

    typeNalUnit = hevcParams.getType(1);
    ASSERT_EQ(typeNalUnit, kHevcNalUnitTypeSps)
            << "Expected NAL type: 33(SPS), found: " << typeNalUnit;

    typeNalUnit = hevcParams.getType(2);
    ASSERT_EQ(typeNalUnit, kHevcNalUnitTypePps)
            << "Expected NAL type: 34(PPS), found: " << typeNalUnit;

    size_t hvccBoxSize = kHvccBoxMaxSize;
    uint8_t *hvcc = (uint8_t *)malloc(kHvccBoxMaxSize);
    ASSERT_NE(hvcc, nullptr) << "Failed to allocate a hvcc buffer of size: " << kHvccBoxMaxSize;

    err = hevcParams.makeHvcc(hvcc, &hvccBoxSize, kNALSizeLength);
    ASSERT_EQ(err, (status_t)OK) << "Unable to create hvcc box";

    ASSERT_GT(hvccBoxSize, kHvccBoxMinSize)
            << "Hvcc box size must be greater than " << kHvccBoxMinSize;

    int16_t frameRate = hvcc[kHvccBoxMinSize - 1] | (hvcc[kHvccBoxMinSize] << 8);
    if (frameRate != mFrameRate)
        cout << "[   WARN   ] Expected frame rate: " << mFrameRate << " Found: " << frameRate
             << endl;

    free(hvcc);
}

// Info File contains the type and length for each chunk/frame
INSTANTIATE_TEST_SUITE_P(
        HEVCUtilsUnitTestAll, HEVCUtilsUnitTest,
        ::testing::Values(make_tuple("crowd_3840x2160p50f300_32500kbps.hevc",
                                     "crowd_3840x2160p50f300_32500kbps.info", 1, 1, 1, 50, false),
                          make_tuple("crowd_1920x1080p24f300_4500kbps.hevc",
                                     "crowd_1920x1080p24f300_4500kbps.info", 1, 1, 1, 24, false),
                          make_tuple("crowd_1280x720p24f300_3000kbps.hevc",
                                     "crowd_1280x720p24f300_3000kbps.info", 1, 1, 1, 24, false),
                          make_tuple("crowd_640x360p24f300_500kbps.hevc",
                                     "crowd_640x360p24f300_500kbps.info", 1, 1, 1, 24, false)));

int main(int argc, char **argv) {
    gEnv = new HEVCUtilsTestEnvironment();
    ::testing::AddGlobalTestEnvironment(gEnv);
    ::testing::InitGoogleTest(&argc, argv);
    int status = gEnv->initFromOptions(argc, argv);
    if (status == 0) {
        status = RUN_ALL_TESTS();
        ALOGV("Test result = %d\n", status);
    }
    return status;
}
+39 −0
Original line number Diff line number Diff line
## Media Testing ##
---
#### HEVC Utils Test
The HEVC Utility Unit Test Suite validates the HevcUtils library available in libstagefright.

Run the following steps to build the test suite:
```
m HEVCUtilsUnitTest
```

The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/

The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/

To test 64-bit binary push binaries from nativetest64.
```
adb push ${OUT}/data/nativetest64/HEVCUtilsUnitTest/HEVCUtilsUnitTest /data/local/tmp/
```

To test 32-bit binary push binaries from nativetest.
```
adb push ${OUT}/data/nativetest/HEVCUtilsUnitTest/HEVCUtilsUnitTest /data/local/tmp/
```

The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/foundation/tests/HEVCUtils/HEVCUtilsUnitTest.zip). Download, unzip and push these files into device for testing.

```
adb push HEVCUtilsUnitTest /data/local/tmp/
```

usage: HEVCUtilsUnitTest -P \<path_to_folder\>
```
adb shell /data/local/tmp/HEVCUtilsUnitTest -P /data/local/tmp/HEVCUtilsUnitTest/
```
Alternatively, the test can also be run using atest command.

```
atest HEVCUtilsUnitTest -- --enable-module-dynamic-download=true
```