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 "Mpeg4H263EncoderTest"
19 #include <utils/Log.h>
20
21 #include <stdint.h>
22 #include <stdio.h>
23 #include <stdlib.h>
24 #include <sys/stat.h>
25
26 #include "mp4enc_api.h"
27
28 #include "Mpeg4H263EncoderTestEnvironment.h"
29
30 #define ENCODED_FILE "/data/local/tmp/Mpeg4H263Output"
31
32 // assuming a worst case compression of 2X
33 constexpr int16_t kCompressionRatio = 2;
34 constexpr int8_t kIDRFrameRefreshIntervalInSec = 1;
35
36 static Mpeg4H263EncoderTestEnvironment *gEnv = nullptr;
37
38 class Mpeg4H263EncoderTest
39 : public ::testing::TestWithParam<tuple<string, bool, int32_t, int32_t, float, int32_t>> {
40 private:
41 void initEncoderParams();
42
43 public:
Mpeg4H263EncoderTest()44 Mpeg4H263EncoderTest()
45 : mInputBuffer(nullptr),
46 mOutputBuffer(nullptr),
47 mFpInput(nullptr),
48 mFpOutput(nullptr),
49 mEncodeHandle(nullptr),
50 mEncodeControl(nullptr) {}
51
~Mpeg4H263EncoderTest()52 ~Mpeg4H263EncoderTest() {
53 if(mFpInput) {
54 fclose(mFpInput);
55 }
56 if(mFpOutput) {
57 fclose(mFpOutput);
58 }
59 if(mInputBuffer) free(mInputBuffer);
60 if(mOutputBuffer) free(mOutputBuffer);
61 if(mEncodeHandle) free(mEncodeHandle);
62 if(mEncodeControl) free(mEncodeControl);
63 }
64
SetUp()65 void SetUp() override {
66 tuple<string /* fileName */, bool /* isMpeg4 */, int32_t /* frameWidth */,
67 int32_t /* frameHeight */, float /* frameRate */, int32_t /* bitRate */>
68 params = GetParam();
69 mFileName = gEnv->getRes() + get<0>(params);
70 mIsMpeg4 = get<1>(params);
71 mFrameWidth = get<2>(params);
72 mFrameHeight = get<3>(params);
73 mFrameRate = get<4>(params);
74 mBitRate = get<5>(params);
75
76 ASSERT_TRUE(mFrameWidth % 16 == 0) << "Frame Width should be multiple of 16";
77 ASSERT_TRUE(mFrameHeight % 16 == 0) << "Frame Height should be multiple of 16";
78 ASSERT_LE(mFrameWidth, (mIsMpeg4 ? 720 : 352))
79 << "Frame Width <= 720 for Mpeg4 and <= 352 for H263";
80 ASSERT_LE(mFrameHeight, (mIsMpeg4 ? 480 : 288))
81 << "Frame Height <= 480 for Mpeg4 and <= 288 for H263";
82 ASSERT_LE(mFrameRate, 30) << "Frame rate less than or equal to 30";
83 ASSERT_LE(mBitRate, 2048) << "Bit rate less than or equal to 2048 kbps";
84
85 mOutputBufferSize = ( mFrameWidth * mFrameHeight * 3/2 ) / kCompressionRatio;
86 mEncodeHandle = new VideoEncOptions;
87 ASSERT_NE(mEncodeHandle, nullptr) << "Failed to get Video Encoding options object";
88 memset(mEncodeHandle, 0, sizeof(VideoEncOptions));
89 mEncodeControl = new VideoEncControls;
90 ASSERT_NE(mEncodeControl, nullptr) << "Failed to get Video Encoding control object";
91 memset(mEncodeControl, 0, sizeof(VideoEncControls));
92 ASSERT_NO_FATAL_FAILURE(initEncoderParams())
93 << "Failed to get the default Encoding parameters!";
94 }
95
96 int64_t getTotalFrames();
97 void processEncoder(int32_t);
98 bool mIsMpeg4;
99 int32_t mFrameWidth, mFrameHeight, mBitRate;
100 int64_t mOutputBufferSize;
101 float mFrameRate;
102 string mFileName;
103 uint8_t *mInputBuffer, *mOutputBuffer;
104 FILE *mFpInput, *mFpOutput;
105 VideoEncOptions *mEncodeHandle;
106 VideoEncControls *mEncodeControl;
107 };
108
initEncoderParams()109 void Mpeg4H263EncoderTest::initEncoderParams() {
110 bool status = PVGetDefaultEncOption(mEncodeHandle, 0);
111 ASSERT_TRUE(status);
112
113 mEncodeHandle->rcType = VBR_1;
114 mEncodeHandle->vbvDelay = 5.0f;
115 mEncodeHandle->profile_level = CORE_PROFILE_LEVEL2;
116 mEncodeHandle->packetSize = 32;
117 mEncodeHandle->rvlcEnable = PV_OFF;
118 mEncodeHandle->numLayers = 1;
119 mEncodeHandle->timeIncRes = 1000;
120 mEncodeHandle->iQuant[0] = 15;
121 mEncodeHandle->pQuant[0] = 12;
122 mEncodeHandle->quantType[0] = 0;
123 mEncodeHandle->noFrameSkipped = PV_OFF;
124 mEncodeHandle->numIntraMB = 0;
125 mEncodeHandle->sceneDetect = PV_ON;
126 mEncodeHandle->searchRange = 16;
127 mEncodeHandle->mv8x8Enable = PV_OFF;
128 mEncodeHandle->gobHeaderInterval = 0;
129 mEncodeHandle->useACPred = PV_ON;
130 mEncodeHandle->intraDCVlcTh = 0;
131 if(!mIsMpeg4) {
132 mEncodeHandle->encMode = H263_MODE;
133 } else {
134 mEncodeHandle->encMode = COMBINE_MODE_WITH_ERR_RES;
135 }
136 mEncodeHandle->encWidth[0] = mFrameWidth;
137 mEncodeHandle->encHeight[0] = mFrameHeight;
138 mEncodeHandle->encFrameRate[0] = mFrameRate;
139 mEncodeHandle->bitRate[0] = mBitRate * 1024;
140 mEncodeHandle->tickPerSrc = mEncodeHandle->timeIncRes / mFrameRate;
141 if (kIDRFrameRefreshIntervalInSec == 0) {
142 // All I frames.
143 mEncodeHandle->intraPeriod = 1;
144 } else {
145 mEncodeHandle->intraPeriod = (kIDRFrameRefreshIntervalInSec * mFrameRate);
146 }
147 }
148
getTotalFrames()149 int64_t Mpeg4H263EncoderTest::getTotalFrames() {
150 int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
151 struct stat buf;
152 stat(mFileName.c_str(), &buf);
153 size_t fileSize = buf.st_size;
154 int64_t totalFrames = (int64_t)(fileSize/frameSize);
155 return totalFrames;
156 }
157
processEncoder(int32_t numFramesToEncode)158 void Mpeg4H263EncoderTest::processEncoder(int32_t numFramesToEncode) {
159 bool status;
160 int64_t numEncodedFrames = 0;
161 int32_t bytesRead;
162 int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
163 while(numFramesToEncode != 0) {
164 bytesRead = fread(mInputBuffer, 1, frameSize, mFpInput);
165 // End of file.
166 if (bytesRead != frameSize) {
167 break;
168 }
169
170 VideoEncFrameIO videoIn, videoOut;
171 videoIn.height = mFrameHeight;
172 videoIn.pitch = mFrameWidth;
173 videoIn.timestamp = (numEncodedFrames * 1000) / mFrameRate; // in ms.
174 videoIn.yChan = mInputBuffer;
175 videoIn.uChan = videoIn.yChan + videoIn.height * videoIn.pitch;
176 videoIn.vChan = videoIn.uChan + ((videoIn.height * videoIn.pitch) >> 2);
177 uint32_t modTimeMs = 0;
178 int32_t dataLength = mOutputBufferSize;
179 int32_t nLayer = 0;
180 status = PVEncodeVideoFrame(mEncodeControl, &videoIn, &videoOut, &modTimeMs, mOutputBuffer,
181 &dataLength, &nLayer);
182 ASSERT_TRUE(status) << "Failed to Encode: " << mFileName;
183
184 MP4HintTrack hintTrack;
185 status = PVGetHintTrack(mEncodeControl, &hintTrack);
186 ASSERT_TRUE(status) << "Failed to get hint track!";
187 UChar *overrunBuffer = PVGetOverrunBuffer(mEncodeControl);
188 ASSERT_EQ(overrunBuffer, nullptr) << "Overrun of buffer!";
189
190 int64_t numBytes = fwrite(mOutputBuffer, 1, dataLength, mFpOutput);
191 ASSERT_EQ(numBytes, dataLength) << "Failed to write to the output file!";
192 numEncodedFrames++;
193 numFramesToEncode--;
194 }
195 }
196
TEST_P(Mpeg4H263EncoderTest,EncodeTest)197 TEST_P(Mpeg4H263EncoderTest, EncodeTest) {
198 mInputBuffer = (uint8_t *)malloc((mFrameWidth * mFrameWidth * 3) / 2);
199 ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate the input buffer!";
200
201 mOutputBuffer = (uint8_t *)malloc(mOutputBufferSize);
202 ASSERT_NE(mOutputBuffer, nullptr) << "Failed to allocate the output buffer!";
203
204 mFpInput = fopen(mFileName.c_str(), "rb");
205 ASSERT_NE(mFpInput, nullptr) << "Failed to open the input file: " << mFileName;
206
207 mFpOutput = fopen(ENCODED_FILE, "wb");
208 ASSERT_NE(mFpOutput, nullptr) << "Failed to open the output file:" << ENCODED_FILE;
209
210 bool status = PVInitVideoEncoder(mEncodeControl, mEncodeHandle);
211 ASSERT_TRUE(status) << "Failed to initialize the encoder!";
212
213 // Get VOL header.
214 int32_t size = mOutputBufferSize;
215 status = PVGetVolHeader(mEncodeControl, mOutputBuffer, &size, 0);
216 ASSERT_TRUE(status) << "Failed to get the VOL header!";
217
218 // Write the VOL header on the first frame.
219 int32_t numBytes = fwrite(mOutputBuffer, 1, size, mFpOutput);
220 ASSERT_EQ(numBytes, size) << "Failed to write the VOL header!";
221
222 int64_t totalFrames = getTotalFrames();
223 ASSERT_NO_FATAL_FAILURE(processEncoder(totalFrames)) << "Failed to Encode: " << mFileName;
224 status = PVCleanUpVideoEncoder(mEncodeControl);
225 ASSERT_TRUE(status) << "Failed to clean up the encoder resources!";
226 }
227
228 INSTANTIATE_TEST_SUITE_P(
229 EncodeTest, Mpeg4H263EncoderTest,
230 ::testing::Values(
231 make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 352, 288, 25, 1024),
232 make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 352, 288, 25, 1024),
233 make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 176, 144, 25, 1024),
234 make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 176, 144, 25, 1024),
235 make_tuple("football_qvga.yuv", false, 352, 288, 25, 1024),
236 make_tuple("football_qvga.yuv", true, 352, 288, 25, 1024),
237 make_tuple("football_qvga.yuv", false, 176, 144, 30, 1024),
238 make_tuple("football_qvga.yuv", true, 176, 144, 30, 1024)));
239
main(int argc,char ** argv)240 int32_t main(int argc, char **argv) {
241 gEnv = new Mpeg4H263EncoderTestEnvironment();
242 ::testing::AddGlobalTestEnvironment(gEnv);
243 ::testing::InitGoogleTest(&argc, argv);
244 uint8_t status = gEnv->initFromOptions(argc, argv);
245 if (status == 0) {
246 status = RUN_ALL_TESTS();
247 ALOGI("Encoder Test Result = %d\n", status);
248 }
249 return status;
250 }
251