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