/* * 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; }