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