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

Commit a55bb622 authored by Ray Essick's avatar Ray Essick Committed by Automerger Merge Worker
Browse files

Merge "MediaTesting: Add Mpeg2ts Unit Test" am: 38325a62

Change-Id: I364e970215afa1014fa213faddef127daa11a28a
parents b67c5df3 38325a62
Loading
Loading
Loading
Loading
+70 −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: "Mpeg2tsUnitTest",
    gtest: true,

    srcs: [
        "Mpeg2tsUnitTest.cpp"
    ],

    shared_libs: [
        "android.hardware.cas@1.0",
        "android.hardware.cas.native@1.0",
        "android.hidl.token@1.0-utils",
        "android.hidl.allocator@1.0",
        "libcrypto",
        "libhidlbase",
        "libhidlmemory",
        "liblog",
        "libmedia",
        "libbinder",
        "libbinder_ndk",
        "libutils",
    ],

    static_libs: [
        "libdatasource",
        "libstagefright",
        "libstagefright_foundation",
        "libstagefright_metadatautils",
        "libstagefright_mpeg2support",
    ],

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

    header_libs: [
        "libmedia_headers",
        "libaudioclient_headers",
    ],

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

    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 Mpeg2ts unit tests">
    <option name="test-suite-tag" value="Mpeg2tsUnitTest" />
    <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
        <option name="cleanup" value="true" />
        <option name="push" value="Mpeg2tsUnitTest->/data/local/tmp/Mpeg2tsUnitTest" />
        <option name="push-file"
            key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/mpeg2ts/test/Mpeg2tsUnitTest.zip?unzip=true"
            value="/data/local/tmp/Mpeg2tsUnitTestRes/" />
    </target_preparer>

    <test class="com.android.tradefed.testtype.GTest" >
        <option name="native-test-device-path" value="/data/local/tmp" />
        <option name="module-name" value="Mpeg2tsUnitTest" />
        <option name="native-test-flag" value="-P /data/local/tmp/Mpeg2tsUnitTestRes/" />
    </test>
</configuration>
+236 −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 "Mpeg2tsUnitTest"

#include <utils/Log.h>

#include <stdint.h>
#include <sys/stat.h>

#include <datasource/FileSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaDataBase.h>
#include <media/stagefright/foundation/AUtils.h>

#include "mpeg2ts/ATSParser.h"
#include "mpeg2ts/AnotherPacketSource.h"

#include "Mpeg2tsUnitTestEnvironment.h"

constexpr size_t kTSPacketSize = 188;
constexpr uint16_t kPIDMask = 0x1FFF;
// Max value of PID which is also used for Null packets
constexpr uint16_t kPIDMaxValue = 8191;
constexpr uint8_t kTSSyncByte = 0x47;
constexpr uint8_t kVideoPresent = 0x01;
constexpr uint8_t kAudioPresent = 0x02;
constexpr uint8_t kMetaDataPresent = 0x04;

static Mpeg2tsUnitTestEnvironment *gEnv = nullptr;

using namespace android;

class Mpeg2tsUnitTest
    : public ::testing ::TestWithParam<
              tuple</*fileName*/ string, /*sourceType*/ char, /*numSource*/ uint16_t>> {
  public:
    Mpeg2tsUnitTest()
        : mInputBuffer(nullptr), mSource(nullptr), mFpInput(nullptr), mParser(nullptr) {}

    ~Mpeg2tsUnitTest() {
        if (mInputBuffer) free(mInputBuffer);
        if (mFpInput) fclose(mFpInput);
        mSource.clear();
    }

    void SetUp() override {
        mOffset = 0;
        mNumDataSource = 0;
        tuple<string, char, uint16_t> params = GetParam();
        char sourceType = get<1>(params);
        /* mSourceType = 0b x x x x x M A V
                                     /  |  \
                            metaData  audio  video */
        mMediaType = (sourceType & 0x07);
        mNumDataSource = get<2>(params);
        string inputFile = gEnv->getRes() + get<0>(params);
        mFpInput = fopen(inputFile.c_str(), "rb");
        ASSERT_NE(mFpInput, nullptr) << "Failed to open file: " << inputFile;

        struct stat buf;
        int8_t err = stat(inputFile.c_str(), &buf);
        ASSERT_EQ(err, 0) << "Failed to get information for file: " << inputFile;

        long fileSize = buf.st_size;
        mTotalPackets = fileSize / kTSPacketSize;
        int32_t fd = fileno(mFpInput);
        ASSERT_GE(fd, 0) << "Failed to get the integer file descriptor";

        mSource = new FileSource(dup(fd), 0, buf.st_size);
        ASSERT_NE(mSource, nullptr) << "Failed to get the data source!";

        mParser = new ATSParser();
        ASSERT_NE(mParser, nullptr) << "Unable to create ATS parser!";
        mInputBuffer = (uint8_t *)malloc(kTSPacketSize);
        ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate memory for TS packet!";
    }

    uint64_t mOffset;
    uint64_t mTotalPackets;
    uint16_t mNumDataSource;

    int8_t mMediaType;

    uint8_t *mInputBuffer;
    string mInputFile;
    sp<DataSource> mSource;
    FILE *mFpInput;
    ATSParser *mParser;
};

TEST_P(Mpeg2tsUnitTest, MediaInfoTest) {
    bool videoFound = false;
    bool audioFound = false;
    bool metaDataFound = false;
    bool syncPointPresent = false;

    int16_t totalDataSource = 0;
    int32_t val32 = 0;
    uint8_t numDataSource = 0;
    uint8_t packet[kTSPacketSize];
    ssize_t numBytesRead = -1;

    ATSParser::SyncEvent event(mOffset);
    static const ATSParser::SourceType mediaType[] = {ATSParser::VIDEO, ATSParser::AUDIO,
                                                      ATSParser::META, ATSParser::NUM_SOURCE_TYPES};
    const uint32_t nMediaTypes = sizeof(mediaType) / sizeof(mediaType[0]);

    while ((numBytesRead = mSource->readAt(mOffset, packet, kTSPacketSize)) == kTSPacketSize) {
        ASSERT_TRUE(packet[0] == kTSSyncByte) << "Sync byte error!";

        // pid is 13 bits
        uint16_t pid = (packet[1] + (packet[2] << 8)) & kPIDMask;
        ASSERT_TRUE(pid <= kPIDMaxValue) << "Invalid PID: " << pid;

        status_t err = mParser->feedTSPacket(packet, kTSPacketSize, &event);
        ASSERT_EQ(err, (status_t)OK) << "Unable to feed TS packet!";

        mOffset += numBytesRead;
        for (int i = 0; i < nMediaTypes; i++) {
            if (mParser->hasSource(mediaType[i])) {
                switch (mediaType[i]) {
                    case ATSParser::VIDEO:
                        videoFound = true;
                        break;
                    case ATSParser::AUDIO:
                        audioFound = true;
                        break;
                    case ATSParser::META:
                        metaDataFound = true;
                        break;
                    case ATSParser::NUM_SOURCE_TYPES:
                        numDataSource = 3;
                        break;
                    default:
                        break;
                }
            }
        }
        if (videoFound && audioFound && metaDataFound && (numDataSource == 3)) break;
    }

    for (int i = 0; i < nMediaTypes; i++) {
        ATSParser::SourceType currentMediaType = mediaType[i];
        if (mParser->hasSource(currentMediaType)) {
            if (event.hasReturnedData()) {
                syncPointPresent = true;
                sp<AnotherPacketSource> syncPacketSource = event.getMediaSource();
                ASSERT_NE(syncPacketSource, nullptr)
                        << "Cannot get sync source for media type: " << currentMediaType;

                status_t err = syncPacketSource->start();
                ASSERT_EQ(err, (status_t)OK) << "Error returned while starting!";

                sp<MetaData> format = syncPacketSource->getFormat();
                ASSERT_NE(format, nullptr) << "Unable to get the format of the source packet!";

                MediaBufferBase *buf;
                syncPacketSource->read(&buf, nullptr);
                ASSERT_NE(buf, nullptr) << "Failed to read sync packet source data";

                MetaDataBase &inMeta = buf->meta_data();
                bool status = inMeta.findInt32(kKeyIsSyncFrame, &val32);
                ASSERT_EQ(status, true) << "Sync frame key is not set";

                status = inMeta.findInt32(kKeyCryptoMode, &val32);
                ASSERT_EQ(status, false) << "Invalid packet, found scrambled packets!";

                err = syncPacketSource->stop();
                ASSERT_EQ(err, (status_t)OK) << "Error returned while stopping!";
            }
            sp<AnotherPacketSource> packetSource = mParser->getSource(currentMediaType);
            ASSERT_NE(packetSource, nullptr)
                    << "Cannot get source for media type: " << currentMediaType;

            status_t err = packetSource->start();
            ASSERT_EQ(err, (status_t)OK) << "Error returned while starting!";
            sp<MetaData> format = packetSource->getFormat();
            ASSERT_NE(format, nullptr) << "Unable to get the format of the packet!";

            err = packetSource->stop();
            ASSERT_EQ(err, (status_t)OK) << "Error returned while stopping!";
        }
    }

    ASSERT_EQ(videoFound, bool(mMediaType & kVideoPresent)) << "No Video packets found!";
    ASSERT_EQ(audioFound, bool(mMediaType & kAudioPresent)) << "No Audio packets found!";
    ASSERT_EQ(metaDataFound, bool(mMediaType & kMetaDataPresent)) << "No meta data found!";

    if (videoFound || audioFound) {
        ASSERT_TRUE(syncPointPresent) << "No sync points found for audio/video";
    }

    if (videoFound) totalDataSource += 1;
    if (audioFound) totalDataSource += 1;
    if (metaDataFound) totalDataSource += 1;

    ASSERT_TRUE(totalDataSource == mNumDataSource)
            << "Expected " << mNumDataSource << " data sources, found " << totalDataSource;
    if (numDataSource == 3) {
        ASSERT_EQ(numDataSource, mNumDataSource)
                << "Expected " << mNumDataSource << " data sources, found " << totalDataSource;
    }
}

INSTANTIATE_TEST_SUITE_P(
        infoTest, Mpeg2tsUnitTest,
        ::testing::Values(make_tuple("crowd_1920x1080_25fps_6700kbps_h264.ts", 0x01, 1),
                          make_tuple("segment000001.ts", 0x03, 2),
                          make_tuple("bbb_44100hz_2ch_128kbps_mp3_5mins.ts", 0x02, 1)));

int32_t main(int argc, char **argv) {
    gEnv = new Mpeg2tsUnitTestEnvironment();
    ::testing::AddGlobalTestEnvironment(gEnv);
    ::testing::InitGoogleTest(&argc, argv);
    uint8_t status = gEnv->initFromOptions(argc, argv);
    if (status == 0) {
        status = RUN_ALL_TESTS();
        ALOGV("Mpeg2tsUnit Test Result = %d\n", status);
    }
    return status;
}
+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 __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
#define __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__

#include <gtest/gtest.h>

#include <getopt.h>

using namespace std;

class Mpeg2tsUnitTestEnvironment : public::testing::Environment {
  public:
    Mpeg2tsUnitTestEnvironment() : 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 Mpeg2tsUnitTestEnvironment::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  // __MPEG2TS_UNIT_TEST_ENVIRONMENT_H__
+38 −0
Original line number Diff line number Diff line
## Media Testing ##
---
#### Mpeg2TS Unit Test :
The Mpeg2TS Unit Test Suite validates the functionality of the libraries present in Mpeg2TS.

Run the following steps to build the test suite:
```
mmm frameworks/av/media/libstagefright/mpeg2ts/test/
```

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/Mpeg2tsUnitTest/Mpeg2tsUnitTest /data/local/tmp/

To test 32-bit binary push binaries from nativetest.

adb push ${OUT}/data/nativetest/Mpeg2tsUnitTest/Mpeg2tsUnitTest /data/local/tmp/

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

```
adb push Mpeg2tsUnitTestRes/. /data/local/tmp/
```

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

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