1 /*
2 * Copyright (C) 2020 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_NDEBUG 0
18 #define LOG_TAG "HevcUtilityTest"
19 #include <utils/Log.h>
20
21 #include <fstream>
22 #include <memory>
23
24 #include <media/stagefright/foundation/ABitReader.h>
25 #include <HevcUtils.h>
26
27 #include "HEVCUtilsTestEnvironment.h"
28
29 using namespace android;
30
31 // max size of hvcc box is 2 KB
32 constexpr uint32_t kHvccBoxMaxSize = 2048;
33 constexpr uint32_t kHvccBoxMinSize = 20;
34 constexpr uint32_t kVPSCode = 32;
35 constexpr uint32_t kSPSCode = 33;
36 constexpr uint32_t kPPSCode = 34;
37 constexpr uint32_t kNALSizeLength = 2;
38
39 static HEVCUtilsTestEnvironment *gEnv = nullptr;
40
41 class HEVCUtilsUnitTest
42 : public ::testing::TestWithParam<
43 tuple</*fileName*/ string, /*infoFileName*/ string, /*numVPSNals*/ size_t,
44 /*numSPSNals*/ size_t, /*numPPSNals*/ size_t, /*frameRate*/ int16_t,
45 /*isHdr*/ bool>> {
46 public:
~HEVCUtilsUnitTest()47 ~HEVCUtilsUnitTest() {
48 if (mMediaFileStream.is_open()) mMediaFileStream.close();
49 if (mInfoFileStream.is_open()) mInfoFileStream.close();
50 }
51
SetUp()52 virtual void SetUp() override {
53 tuple<string, string, size_t, size_t, size_t, int16_t, bool> params = GetParam();
54 string inputMediaFile = gEnv->getRes() + get<0>(params);
55 mMediaFileStream.open(inputMediaFile, ifstream::in);
56 ASSERT_TRUE(mMediaFileStream.is_open()) << "Failed to open media file: " << inputMediaFile;
57
58 string inputInfoFile = gEnv->getRes() + get<1>(params);
59 mInfoFileStream.open(inputInfoFile, ifstream::in);
60 ASSERT_TRUE(mInfoFileStream.is_open()) << "Failed to open info file: " << inputInfoFile;
61
62 mNumVPSNals = get<2>(params);
63 mNumSPSNals = get<3>(params);
64 mNumPPSNals = get<4>(params);
65 mFrameRate = get<5>(params);
66 mIsHDR = get<6>(params);
67 }
68
69 size_t mNumVPSNals;
70 size_t mNumSPSNals;
71 size_t mNumPPSNals;
72 int16_t mFrameRate;
73 bool mIsHDR;
74 ifstream mMediaFileStream;
75 ifstream mInfoFileStream;
76 };
77
TEST_P(HEVCUtilsUnitTest,NALUnitTest)78 TEST_P(HEVCUtilsUnitTest, NALUnitTest) {
79 HevcParameterSets hevcParams;
80
81 string line;
82 int32_t index = 0;
83 status_t err;
84 while (getline(mInfoFileStream, line)) {
85 string type;
86 int32_t chunkLength;
87
88 istringstream stringLine(line);
89 stringLine >> type >> chunkLength;
90 ASSERT_GT(chunkLength, 0) << "Length of data chunk must be greater than 0";
91
92 std::unique_ptr<char[]> data(new char[chunkLength]);
93 ASSERT_NE(data, nullptr) << "Failed to allocate data buffer of size: " << chunkLength;
94
95 mMediaFileStream.read(data.get(), chunkLength);
96 ASSERT_EQ(mMediaFileStream.gcount(), chunkLength)
97 << "Failed to read complete file, bytes read: " << mMediaFileStream.gcount();
98
99 // A valid startcode consists of at least two 0x00 bytes followed by 0x01.
100 int32_t offset = 0;
101 for (; offset + 2 < chunkLength; ++offset) {
102 if (data[offset + 2] == 0x01 && data[offset + 1] == 0x00 && data[offset] == 0x00) {
103 break;
104 }
105 }
106 offset += 3;
107 ASSERT_LE(offset, chunkLength) << "NAL unit offset must not exceed the chunk length";
108
109 uint8_t *nalUnit = (uint8_t *)(data.get() + offset);
110 size_t nalUnitLength = chunkLength - offset;
111
112 // Add NAL units only if they're of type: VPS/SPS/PPS/SEI
113 if (!((type.compare("VPS") && type.compare("SPS") && type.compare("PPS") &&
114 type.compare("SEI")))) {
115 err = hevcParams.addNalUnit(nalUnit, nalUnitLength);
116 ASSERT_EQ(err, (status_t)OK)
117 << "Failed to add NAL Unit type: " << type << " Size: " << nalUnitLength;
118
119 size_t sizeNalUnit = hevcParams.getSize(index);
120 ASSERT_EQ(sizeNalUnit, nalUnitLength) << "Invalid size returned for NAL: " << type;
121
122 std::unique_ptr<uint8_t[]> destination(new uint8_t[nalUnitLength]);
123 ASSERT_NE(destination, nullptr)
124 << "Failed to allocate buffer of size: " << nalUnitLength;
125
126 bool status = hevcParams.write(index, destination.get(), nalUnitLength);
127 ASSERT_TRUE(status) << "Unable to write NAL Unit data";
128
129 index++;
130 } else {
131 err = hevcParams.addNalUnit(nalUnit, nalUnitLength);
132 ASSERT_NE(err, (status_t)OK) << "Invalid NAL Unit added, type: " << type;
133 }
134 }
135
136 size_t numNalUnits = hevcParams.getNumNalUnitsOfType(kVPSCode);
137 ASSERT_EQ(numNalUnits, mNumVPSNals) << "Wrong number of VPS NAL Units";
138
139 numNalUnits = hevcParams.getNumNalUnitsOfType(kSPSCode);
140 ASSERT_EQ(numNalUnits, mNumSPSNals) << "Wrong number of SPS NAL Units";
141
142 numNalUnits = hevcParams.getNumNalUnitsOfType(kPPSCode);
143 ASSERT_EQ(numNalUnits, mNumPPSNals) << "Wrong number of PPS NAL Units";
144
145 HevcParameterSets::Info info = hevcParams.getInfo();
146 ASSERT_EQ(info & HevcParameterSets::kInfoIsHdr,
147 (mIsHDR ? HevcParameterSets::kInfoIsHdr : HevcParameterSets::kInfoNone))
148 << "Wrong info about HDR";
149
150 ASSERT_EQ(info & HevcParameterSets::kInfoHasColorDescription,
151 (mIsHDR ? HevcParameterSets::kInfoHasColorDescription : HevcParameterSets::kInfoNone))
152 << "Wrong info about color description";
153
154 // an HEVC file starts with VPS, SPS and PPS NAL units in sequence.
155 uint8_t typeNalUnit = hevcParams.getType(0);
156 ASSERT_EQ(typeNalUnit, kHevcNalUnitTypeVps)
157 << "Expected NAL type: 32(VPS), found: " << typeNalUnit;
158
159 typeNalUnit = hevcParams.getType(1);
160 ASSERT_EQ(typeNalUnit, kHevcNalUnitTypeSps)
161 << "Expected NAL type: 33(SPS), found: " << typeNalUnit;
162
163 typeNalUnit = hevcParams.getType(2);
164 ASSERT_EQ(typeNalUnit, kHevcNalUnitTypePps)
165 << "Expected NAL type: 34(PPS), found: " << typeNalUnit;
166
167 size_t hvccBoxSize = kHvccBoxMaxSize;
168 std::unique_ptr<uint8_t[]> hvcc(new uint8_t[kHvccBoxMaxSize]);
169 ASSERT_NE(hvcc, nullptr) << "Failed to allocate a hvcc buffer of size: " << kHvccBoxMaxSize;
170
171 err = hevcParams.makeHvcc(hvcc.get(), &hvccBoxSize, kNALSizeLength);
172 ASSERT_EQ(err, (status_t)OK) << "Unable to create hvcc box";
173
174 ASSERT_GT(hvccBoxSize, kHvccBoxMinSize)
175 << "Hvcc box size must be greater than " << kHvccBoxMinSize;
176
177 int16_t frameRate = hvcc[kHvccBoxMinSize - 1] | (hvcc[kHvccBoxMinSize] << 8);
178 if (frameRate != mFrameRate)
179 cout << "[ WARN ] Expected frame rate: " << mFrameRate << " Found: " << frameRate
180 << endl;
181 }
182
183 // Info File contains the type and length for each chunk/frame
184 INSTANTIATE_TEST_SUITE_P(
185 HEVCUtilsUnitTestAll, HEVCUtilsUnitTest,
186 ::testing::Values(make_tuple("crowd_3840x2160p50f300_32500kbps.hevc",
187 "crowd_3840x2160p50f300_32500kbps.info", 1, 1, 1, 50, false),
188 make_tuple("crowd_1920x1080p24f300_4500kbps.hevc",
189 "crowd_1920x1080p24f300_4500kbps.info", 1, 1, 1, 24, false),
190 make_tuple("crowd_1280x720p24f300_3000kbps.hevc",
191 "crowd_1280x720p24f300_3000kbps.info", 1, 1, 1, 24, false),
192 make_tuple("crowd_640x360p24f300_500kbps.hevc",
193 "crowd_640x360p24f300_500kbps.info", 1, 1, 1, 24, false)));
194
main(int argc,char ** argv)195 int main(int argc, char **argv) {
196 gEnv = new HEVCUtilsTestEnvironment();
197 ::testing::AddGlobalTestEnvironment(gEnv);
198 ::testing::InitGoogleTest(&argc, argv);
199 int status = gEnv->initFromOptions(argc, argv);
200 if (status == 0) {
201 status = RUN_ALL_TESTS();
202 ALOGV("Test result = %d\n", status);
203 }
204 return status;
205 }
206