Merge "MediaTesting: Add Mpeg4 H263 Encoder Test" am: 7bf3eff877 am: 0d431dd1e1
Change-Id: I2f782c4b4b864f76f1b6bcfea440c33a024f299b
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp b/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp
new file mode 100644
index 0000000..b9a8117
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Android.bp
@@ -0,0 +1,45 @@
+/*
+ * 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.
+ */
+
+cc_test {
+ name: "Mpeg4H263EncoderTest",
+ gtest: true,
+
+ srcs : [ "Mpeg4H263EncoderTest.cpp" ],
+
+ shared_libs: [
+ "libutils",
+ "liblog",
+ ],
+
+ static_libs: [
+ "libstagefright_m4vh263enc",
+ ],
+
+ cflags: [
+ "-DOSCL_IMPORT_REF=",
+ "-Wall",
+ "-Werror",
+ ],
+
+ sanitize: {
+ misc_undefined: [
+ "signed-integer-overflow",
+ "unsigned-integer-overflow",
+ ],
+ cfi: true,
+ },
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml b/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml
new file mode 100644
index 0000000..5218932
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/AndroidTest.xml
@@ -0,0 +1,31 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- 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.
+-->
+<configuration description="Test module config for MPEG4H263 encoder unit tests">
+ <option name="test-suite-tag" value="Mpeg4H263EncoderTest" />
+ <target_preparer class="com.android.tradefed.targetprep.PushFilePreparer">
+ <option name="cleanup" value="true" />
+ <option name="push" value="Mpeg4H263EncoderTest->/data/local/tmp/Mpeg4H263EncoderTest/" />
+ <option name="push-file"
+ key="https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263Encoder.zip?unzip=true"
+ value="/data/local/tmp/Mpeg4H263EncoderTestRes/" />
+ </target_preparer>
+
+ <test class="com.android.tradefed.testtype.GTest" >
+ <option name="native-test-device-path" value="/data/local/tmp" />
+ <option name="module-name" value="Mpeg4H263EncoderTest" />
+ <option name="native-test-flag" value="-P /data/local/tmp/Mpeg4H263EncoderTestRes/" />
+ </test>
+</configuration>
\ No newline at end of file
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp
new file mode 100644
index 0000000..78c705a
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTest.cpp
@@ -0,0 +1,250 @@
+/*
+ * 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;
+}
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h
new file mode 100644
index 0000000..7ee36e0
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263EncoderTestEnvironment.h
@@ -0,0 +1,73 @@
+/*
+ * 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.
+ */
+
+#ifndef __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
+#define __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
+
+#include <gtest/gtest.h>
+
+#include <getopt.h>
+
+using namespace std;
+
+class Mpeg4H263EncoderTestEnvironment : public::testing::Environment {
+ public:
+ Mpeg4H263EncoderTestEnvironment() : res("/data/local/tmp/Mpeg4H263EncoderTest/") {}
+
+ // Parses the command line arguments
+ int initFromOptions(int argc, char **argv);
+
+ void setRes(const char *_res) { res = _res; }
+
+ const string getRes() const { return res; }
+
+ private:
+ string res;
+};
+
+int Mpeg4H263EncoderTestEnvironment::initFromOptions(int argc, char **argv) {
+ static struct option options[] = {{"path", required_argument, 0, 'P'}, {0, 0, 0, 0}};
+
+ while (true) {
+ int index = 0;
+ int c = getopt_long(argc, argv, "P:", options, &index);
+ if (c == -1) {
+ break;
+ }
+
+ switch (c) {
+ case 'P': {
+ setRes(optarg);
+ break;
+ }
+ default:
+ break;
+ }
+ }
+
+ if (optind < argc) {
+ fprintf(stderr,
+ "unrecognized option: %s\n\n"
+ "usage: %s <gtest options> <test options>\n\n"
+ "test options are:\n\n"
+ "-P, --path: Resource files directory location\n",
+ argv[optind ?: 1], argv[0]);
+ return 2;
+ }
+ return 0;
+}
+
+#endif // __MPEG4_H263_ENCODER_TEST_ENVIRONMENT_H__
diff --git a/media/libstagefright/codecs/m4v_h263/enc/test/README.md b/media/libstagefright/codecs/m4v_h263/enc/test/README.md
new file mode 100644
index 0000000..25de878
--- /dev/null
+++ b/media/libstagefright/codecs/m4v_h263/enc/test/README.md
@@ -0,0 +1,38 @@
+## Media Testing ##
+---
+
+#### Mpeg4H263Encoder :
+The Mpeg4H263Encoder Test Suite validates the Mpeg4 and H263 encoder available in libstagefright.
+Run the following steps to build the test suite:
+```
+m Mpeg4H263EncoderTest
+```
+
+The 32-bit binaries will be created in the following path : ${OUT}/data/nativetest/
+The 64-bit binaries will be created in the following path : ${OUT}/data/nativetest64/
+
+To test 64-bit binary push binaries from nativetest64.
+```
+adb push ${OUT}/data/nativetest64/Mpeg4H263EncoderTest/Mpeg4H263EncoderTest /data/local/tmp/
+```
+
+To test 32-bit binary push binaries from nativetest.
+```
+adb push ${OUT}/data/nativetest/Mpeg4H263EncoderTest/Mpeg4H263EncoderTest /data/local/tmp/
+```
+
+The resource file for the tests is taken from [here](https://storage.googleapis.com/android_media/frameworks/av/media/libstagefright/codecs/m4v_h263/enc/test/Mpeg4H263Encoder.zip ) Download, unzip and push these files into device for testing.
+
+```
+adb push Mpeg4H263Encoder/. /data/local/tmp/
+```
+
+usage: Mpeg4H263EncoderTest -P \<path_to_folder\>
+```
+adb shell /data/local/tmp/Mpeg4H263EncoderTest -P /data/local/tmp/
+```
+Alternatively, the test can also be run using atest command.
+
+```
+atest Mpeg4H263EncoderTest -- --enable-module-dynamic-download=true
+```