blob: c43e1f839a2591330d6f62603331402da7ee60d7 [file] [log] [blame]
/*
* 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 <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;
}