blob: 78c705acbbdbfafc942f71e7ba977a248335b96a [file] [log] [blame]
/*
* Copyright (C) 2020 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "Mpeg4H263EncoderTest"
#include <utils/Log.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include "mp4enc_api.h"
#include "Mpeg4H263EncoderTestEnvironment.h"
#define ENCODED_FILE "/data/local/tmp/Mpeg4H263Output"
// assuming a worst case compression of 2X
constexpr int16_t kCompressionRatio = 2;
constexpr int8_t kIDRFrameRefreshIntervalInSec = 1;
static Mpeg4H263EncoderTestEnvironment *gEnv = nullptr;
class Mpeg4H263EncoderTest
: public ::testing::TestWithParam<tuple<string, bool, int32_t, int32_t, float, int32_t>> {
private:
void initEncoderParams();
public:
Mpeg4H263EncoderTest()
: mInputBuffer(nullptr),
mOutputBuffer(nullptr),
mFpInput(nullptr),
mFpOutput(nullptr),
mEncodeHandle(nullptr),
mEncodeControl(nullptr) {}
~Mpeg4H263EncoderTest() {
if(mFpInput) {
fclose(mFpInput);
}
if(mFpOutput) {
fclose(mFpOutput);
}
if(mInputBuffer) free(mInputBuffer);
if(mOutputBuffer) free(mOutputBuffer);
if(mEncodeHandle) free(mEncodeHandle);
if(mEncodeControl) free(mEncodeControl);
}
void SetUp() override {
tuple<string /* fileName */, bool /* isMpeg4 */, int32_t /* frameWidth */,
int32_t /* frameHeight */, float /* frameRate */, int32_t /* bitRate */>
params = GetParam();
mFileName = gEnv->getRes() + get<0>(params);
mIsMpeg4 = get<1>(params);
mFrameWidth = get<2>(params);
mFrameHeight = get<3>(params);
mFrameRate = get<4>(params);
mBitRate = get<5>(params);
ASSERT_TRUE(mFrameWidth % 16 == 0) << "Frame Width should be multiple of 16";
ASSERT_TRUE(mFrameHeight % 16 == 0) << "Frame Height should be multiple of 16";
ASSERT_LE(mFrameWidth, (mIsMpeg4 ? 720 : 352))
<< "Frame Width <= 720 for Mpeg4 and <= 352 for H263";
ASSERT_LE(mFrameHeight, (mIsMpeg4 ? 480 : 288))
<< "Frame Height <= 480 for Mpeg4 and <= 288 for H263";
ASSERT_LE(mFrameRate, 30) << "Frame rate less than or equal to 30";
ASSERT_LE(mBitRate, 2048) << "Bit rate less than or equal to 2048 kbps";
mOutputBufferSize = ( mFrameWidth * mFrameHeight * 3/2 ) / kCompressionRatio;
mEncodeHandle = new VideoEncOptions;
ASSERT_NE(mEncodeHandle, nullptr) << "Failed to get Video Encoding options object";
memset(mEncodeHandle, 0, sizeof(VideoEncOptions));
mEncodeControl = new VideoEncControls;
ASSERT_NE(mEncodeControl, nullptr) << "Failed to get Video Encoding control object";
memset(mEncodeControl, 0, sizeof(VideoEncControls));
ASSERT_NO_FATAL_FAILURE(initEncoderParams())
<< "Failed to get the default Encoding parameters!";
}
int64_t getTotalFrames();
void processEncoder(int32_t);
bool mIsMpeg4;
int32_t mFrameWidth, mFrameHeight, mBitRate;
int64_t mOutputBufferSize;
float mFrameRate;
string mFileName;
uint8_t *mInputBuffer, *mOutputBuffer;
FILE *mFpInput, *mFpOutput;
VideoEncOptions *mEncodeHandle;
VideoEncControls *mEncodeControl;
};
void Mpeg4H263EncoderTest::initEncoderParams() {
bool status = PVGetDefaultEncOption(mEncodeHandle, 0);
ASSERT_TRUE(status);
mEncodeHandle->rcType = VBR_1;
mEncodeHandle->vbvDelay = 5.0f;
mEncodeHandle->profile_level = CORE_PROFILE_LEVEL2;
mEncodeHandle->packetSize = 32;
mEncodeHandle->rvlcEnable = PV_OFF;
mEncodeHandle->numLayers = 1;
mEncodeHandle->timeIncRes = 1000;
mEncodeHandle->iQuant[0] = 15;
mEncodeHandle->pQuant[0] = 12;
mEncodeHandle->quantType[0] = 0;
mEncodeHandle->noFrameSkipped = PV_OFF;
mEncodeHandle->numIntraMB = 0;
mEncodeHandle->sceneDetect = PV_ON;
mEncodeHandle->searchRange = 16;
mEncodeHandle->mv8x8Enable = PV_OFF;
mEncodeHandle->gobHeaderInterval = 0;
mEncodeHandle->useACPred = PV_ON;
mEncodeHandle->intraDCVlcTh = 0;
if(!mIsMpeg4) {
mEncodeHandle->encMode = H263_MODE;
} else {
mEncodeHandle->encMode = COMBINE_MODE_WITH_ERR_RES;
}
mEncodeHandle->encWidth[0] = mFrameWidth;
mEncodeHandle->encHeight[0] = mFrameHeight;
mEncodeHandle->encFrameRate[0] = mFrameRate;
mEncodeHandle->bitRate[0] = mBitRate * 1024;
mEncodeHandle->tickPerSrc = mEncodeHandle->timeIncRes / mFrameRate;
if (kIDRFrameRefreshIntervalInSec == 0) {
// All I frames.
mEncodeHandle->intraPeriod = 1;
} else {
mEncodeHandle->intraPeriod = (kIDRFrameRefreshIntervalInSec * mFrameRate);
}
}
int64_t Mpeg4H263EncoderTest::getTotalFrames() {
int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
struct stat buf;
stat(mFileName.c_str(), &buf);
size_t fileSize = buf.st_size;
int64_t totalFrames = (int64_t)(fileSize/frameSize);
return totalFrames;
}
void Mpeg4H263EncoderTest::processEncoder(int32_t numFramesToEncode) {
bool status;
int64_t numEncodedFrames = 0;
int32_t bytesRead;
int32_t frameSize = (mFrameWidth * mFrameHeight * 3) / 2;
while(numFramesToEncode != 0) {
bytesRead = fread(mInputBuffer, 1, frameSize, mFpInput);
// End of file.
if (bytesRead != frameSize) {
break;
}
VideoEncFrameIO videoIn, videoOut;
videoIn.height = mFrameHeight;
videoIn.pitch = mFrameWidth;
videoIn.timestamp = (numEncodedFrames * 1000) / mFrameRate; // in ms.
videoIn.yChan = mInputBuffer;
videoIn.uChan = videoIn.yChan + videoIn.height * videoIn.pitch;
videoIn.vChan = videoIn.uChan + ((videoIn.height * videoIn.pitch) >> 2);
uint32_t modTimeMs = 0;
int32_t dataLength = mOutputBufferSize;
int32_t nLayer = 0;
status = PVEncodeVideoFrame(mEncodeControl, &videoIn, &videoOut, &modTimeMs, mOutputBuffer,
&dataLength, &nLayer);
ASSERT_TRUE(status) << "Failed to Encode: " << mFileName;
MP4HintTrack hintTrack;
status = PVGetHintTrack(mEncodeControl, &hintTrack);
ASSERT_TRUE(status) << "Failed to get hint track!";
UChar *overrunBuffer = PVGetOverrunBuffer(mEncodeControl);
ASSERT_EQ(overrunBuffer, nullptr) << "Overrun of buffer!";
int64_t numBytes = fwrite(mOutputBuffer, 1, dataLength, mFpOutput);
ASSERT_EQ(numBytes, dataLength) << "Failed to write to the output file!";
numEncodedFrames++;
numFramesToEncode--;
}
}
TEST_P(Mpeg4H263EncoderTest, EncodeTest) {
mInputBuffer = (uint8_t *)malloc((mFrameWidth * mFrameWidth * 3) / 2);
ASSERT_NE(mInputBuffer, nullptr) << "Failed to allocate the input buffer!";
mOutputBuffer = (uint8_t *)malloc(mOutputBufferSize);
ASSERT_NE(mOutputBuffer, nullptr) << "Failed to allocate the output buffer!";
mFpInput = fopen(mFileName.c_str(), "rb");
ASSERT_NE(mFpInput, nullptr) << "Failed to open the input file: " << mFileName;
mFpOutput = fopen(ENCODED_FILE, "wb");
ASSERT_NE(mFpOutput, nullptr) << "Failed to open the output file:" << ENCODED_FILE;
bool status = PVInitVideoEncoder(mEncodeControl, mEncodeHandle);
ASSERT_TRUE(status) << "Failed to initialize the encoder!";
// Get VOL header.
int32_t size = mOutputBufferSize;
status = PVGetVolHeader(mEncodeControl, mOutputBuffer, &size, 0);
ASSERT_TRUE(status) << "Failed to get the VOL header!";
// Write the VOL header on the first frame.
int32_t numBytes = fwrite(mOutputBuffer, 1, size, mFpOutput);
ASSERT_EQ(numBytes, size) << "Failed to write the VOL header!";
int64_t totalFrames = getTotalFrames();
ASSERT_NO_FATAL_FAILURE(processEncoder(totalFrames)) << "Failed to Encode: " << mFileName;
status = PVCleanUpVideoEncoder(mEncodeControl);
ASSERT_TRUE(status) << "Failed to clean up the encoder resources!";
}
INSTANTIATE_TEST_SUITE_P(
EncodeTest, Mpeg4H263EncoderTest,
::testing::Values(
make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 352, 288, 25, 1024),
make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 352, 288, 25, 1024),
make_tuple("bbb_352x288_420p_30fps_32frames.yuv", false, 176, 144, 25, 1024),
make_tuple("bbb_352x288_420p_30fps_32frames.yuv", true, 176, 144, 25, 1024),
make_tuple("football_qvga.yuv", false, 352, 288, 25, 1024),
make_tuple("football_qvga.yuv", true, 352, 288, 25, 1024),
make_tuple("football_qvga.yuv", false, 176, 144, 30, 1024),
make_tuple("football_qvga.yuv", true, 176, 144, 30, 1024)));
int32_t main(int argc, char **argv) {
gEnv = new Mpeg4H263EncoderTestEnvironment();
::testing::AddGlobalTestEnvironment(gEnv);
::testing::InitGoogleTest(&argc, argv);
uint8_t status = gEnv->initFromOptions(argc, argv);
if (status == 0) {
status = RUN_ALL_TESTS();
ALOGI("Encoder Test Result = %d\n", status);
}
return status;
}