251 lines
9.6 KiB
C++
251 lines
9.6 KiB
C++
/*
|
|
* 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;
|
|
}
|