xref: /aosp_15_r20/frameworks/av/media/libstagefright/tests/HEVC/HEVCUtilsUnitTest.cpp (revision ec779b8e0859a360c3d303172224686826e6e0e1)
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