Loading media/libstagefright/codecs/m4v_h263/dec/test/Android.bp 0 → 100644 +47 −0 Original line number Diff line number Diff line /* * 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: "Mpeg4H263DecoderTest", gtest: true, srcs: [ "Mpeg4H263DecoderTest.cpp", ], shared_libs: [ "liblog", ], static_libs: [ "libstagefright_m4vh263dec", "libstagefright_foundation", ], cflags: [ "-DOSCL_IMPORT_REF=", "-Werror", "-Wall", ], sanitize: { misc_undefined: [ "unsigned-integer-overflow", "signed-integer-overflow", ], cfi: true, }, } media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTest.cpp 0 → 100644 +423 −0 Original line number Diff line number Diff line /* * 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 "Mpeg4H263DecoderTest" #include <utils/Log.h> #include <stdio.h> #include <string.h> #include <utils/String8.h> #include <fstream> #include <media/stagefright/foundation/AUtils.h> #include "mp4dec_api.h" #include "Mpeg4H263DecoderTestEnvironment.h" using namespace android; #define OUTPUT_FILE_NAME "/data/local/tmp/Output.yuv" #define CODEC_CONFIG_FLAG 32 #define SYNC_FRAME 1 #define MPEG4_MAX_WIDTH 1920 #define MPEG4_MAX_HEIGHT 1080 #define H263_MAX_WIDTH 352 #define H263_MAX_HEIGHT 288 constexpr uint32_t kNumOutputBuffers = 2; struct FrameInfo { int32_t bytesCount; uint32_t flags; int64_t timestamp; }; struct tagvideoDecControls; static Mpeg4H263DecoderTestEnvironment *gEnv = nullptr; class Mpeg4H263DecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> { public: Mpeg4H263DecoderTest() : mDecHandle(nullptr), mInputBuffer(nullptr), mInitialized(false), mFramesConfigured(false), mNumSamplesOutput(0), mWidth(352), mHeight(288) { memset(mOutputBuffer, 0x0, sizeof(mOutputBuffer)); } ~Mpeg4H263DecoderTest() { if (mEleStream.is_open()) mEleStream.close(); if (mDecHandle) { delete mDecHandle; mDecHandle = nullptr; } if (mInputBuffer) { free(mInputBuffer); mInputBuffer = nullptr; } freeOutputBuffer(); } status_t initDecoder(); void allocOutputBuffer(size_t outputBufferSize); void dumpOutput(ofstream &ostrm); void freeOutputBuffer(); void processMpeg4H263Decoder(vector<FrameInfo> Info, int32_t offset, int32_t range, ifstream &mEleStream, ofstream &ostrm, MP4DecodingMode inputMode); void deInitDecoder(); ifstream mEleStream; tagvideoDecControls *mDecHandle; char *mInputBuffer; uint8_t *mOutputBuffer[kNumOutputBuffers]; bool mInitialized; bool mFramesConfigured; uint32_t mNumSamplesOutput; uint32_t mWidth; uint32_t mHeight; }; status_t Mpeg4H263DecoderTest::initDecoder() { if (!mDecHandle) { mDecHandle = new tagvideoDecControls; } if (!mDecHandle) { return NO_MEMORY; } memset(mDecHandle, 0, sizeof(tagvideoDecControls)); return OK; } void Mpeg4H263DecoderTest::allocOutputBuffer(size_t outputBufferSize) { for (int32_t i = 0; i < kNumOutputBuffers; ++i) { if (!mOutputBuffer[i]) { mOutputBuffer[i] = (uint8_t *)malloc(outputBufferSize); ASSERT_NE(mOutputBuffer[i], nullptr) << "Output buffer allocation failed"; } } } void Mpeg4H263DecoderTest::dumpOutput(ofstream &ostrm) { uint8_t *src = mOutputBuffer[mNumSamplesOutput & 1]; size_t vStride = align(mHeight, 16); size_t srcYStride = align(mWidth, 16); size_t srcUVStride = srcYStride / 2; uint8_t *srcStart = src; /* Y buffer */ for (size_t i = 0; i < mHeight; ++i) { ostrm.write(reinterpret_cast<char *>(src), mWidth); src += srcYStride; } /* U buffer */ src = srcStart + vStride * srcYStride; for (size_t i = 0; i < mHeight / 2; ++i) { ostrm.write(reinterpret_cast<char *>(src), mWidth / 2); src += srcUVStride; } /* V buffer */ src = srcStart + vStride * srcYStride * 5 / 4; for (size_t i = 0; i < mHeight / 2; ++i) { ostrm.write(reinterpret_cast<char *>(src), mWidth / 2); src += srcUVStride; } } void Mpeg4H263DecoderTest::freeOutputBuffer() { for (int32_t i = 0; i < kNumOutputBuffers; ++i) { if (mOutputBuffer[i]) { free(mOutputBuffer[i]); mOutputBuffer[i] = nullptr; } } } void Mpeg4H263DecoderTest::processMpeg4H263Decoder(vector<FrameInfo> Info, int32_t offset, int32_t range, ifstream &mEleStream, ofstream &ostrm, MP4DecodingMode inputMode) { size_t maxWidth = (inputMode == MPEG4_MODE) ? MPEG4_MAX_WIDTH : H263_MAX_WIDTH; size_t maxHeight = (inputMode == MPEG4_MODE) ? MPEG4_MAX_HEIGHT : H263_MAX_HEIGHT; size_t outputBufferSize = align(maxWidth, 16) * align(maxHeight, 16) * 3 / 2; uint32_t frameIndex = offset; bool status = true; ASSERT_GE(range, 0) << "Invalid range"; ASSERT_TRUE(offset >= 0 && offset <= Info.size() - 1) << "Invalid offset"; ASSERT_LE(range + offset, Info.size()) << "range+offset can't be greater than the no of frames"; while (1) { if (frameIndex == Info.size() || frameIndex == (offset + range)) break; int32_t bytesCount = Info[frameIndex].bytesCount; ASSERT_GT(bytesCount, 0) << "Size for the memory allocation is negative"; mInputBuffer = (char *)malloc(bytesCount); ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory to read frame"; mEleStream.read(mInputBuffer, bytesCount); ASSERT_EQ(mEleStream.gcount(), bytesCount) << "mEleStream.gcount() != bytesCount"; static const uint8_t volInfo[] = {0x00, 0x00, 0x01, 0xB0}; bool volHeader = memcmp(mInputBuffer, volInfo, 4) == 0; if (volHeader) { PVCleanUpVideoDecoder(mDecHandle); mInitialized = false; } if (!mInitialized) { uint8_t *volData[1]{}; int32_t volSize = 0; uint32_t flags = Info[frameIndex].flags; bool codecConfig = flags == CODEC_CONFIG_FLAG; if (codecConfig || volHeader) { volData[0] = reinterpret_cast<uint8_t *>(mInputBuffer); volSize = bytesCount; } status = PVInitVideoDecoder(mDecHandle, volData, &volSize, 1, maxWidth, maxHeight, inputMode); ASSERT_TRUE(status) << "PVInitVideoDecoder failed. Unsupported content"; mInitialized = true; MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle); ASSERT_EQ(inputMode, actualMode) << "Decoded mode not same as actual mode of the decoder"; PVSetPostProcType(mDecHandle, 0); int32_t dispWidth, dispHeight; PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight); int32_t bufWidth, bufHeight; PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight); ASSERT_LE(dispWidth, bufWidth) << "Display width is greater than buffer width"; ASSERT_LE(dispHeight, bufHeight) << "Display height is greater than buffer height"; if (dispWidth != mWidth || dispHeight != mHeight) { mWidth = dispWidth; mHeight = dispHeight; freeOutputBuffer(); if (inputMode == H263_MODE) { PVCleanUpVideoDecoder(mDecHandle); uint8_t *volData[1]{}; int32_t volSize = 0; status = PVInitVideoDecoder(mDecHandle, volData, &volSize, 1, maxWidth, maxHeight, H263_MODE); ASSERT_TRUE(status) << "PVInitVideoDecoder failed for H263"; } mFramesConfigured = false; } if (codecConfig) { frameIndex++; continue; } } uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size; ASSERT_GE(outputBufferSize, yFrameSize * 3 / 2) << "Too small output buffer: " << outputBufferSize << " bytes"; ASSERT_NO_FATAL_FAILURE(allocOutputBuffer(outputBufferSize)); if (!mFramesConfigured) { PVSetReferenceYUV(mDecHandle, mOutputBuffer[1]); mFramesConfigured = true; } // Need to check if header contains new info, e.g., width/height, etc. VopHeaderInfo headerInfo; uint32_t useExtTimestamp = 1; int32_t inputSize = (Info)[frameIndex].bytesCount; uint32_t timestamp = frameIndex; uint8_t *bitstreamTmp = reinterpret_cast<uint8_t *>(mInputBuffer); status = PVDecodeVopHeader(mDecHandle, &bitstreamTmp, ×tamp, &inputSize, &headerInfo, &useExtTimestamp, mOutputBuffer[mNumSamplesOutput & 1]); ASSERT_EQ(status, PV_TRUE) << "failed to decode vop header"; // H263 doesn't have VOL header, the frame size information is in short header, i.e. the // decoder may detect size change after PVDecodeVopHeader. int32_t dispWidth, dispHeight; PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight); int32_t bufWidth, bufHeight; PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight); ASSERT_LE(dispWidth, bufWidth) << "Display width is greater than buffer width"; ASSERT_LE(dispHeight, bufHeight) << "Display height is greater than buffer height"; if (dispWidth != mWidth || dispHeight != mHeight) { mWidth = dispWidth; mHeight = dispHeight; } status = PVDecodeVopBody(mDecHandle, &inputSize); ASSERT_EQ(status, PV_TRUE) << "failed to decode video frame No = %d" << frameIndex; dumpOutput(ostrm); ++mNumSamplesOutput; ++frameIndex; } freeOutputBuffer(); } void Mpeg4H263DecoderTest::deInitDecoder() { if (mInitialized) { if (mDecHandle) { PVCleanUpVideoDecoder(mDecHandle); delete mDecHandle; mDecHandle = nullptr; } mInitialized = false; } freeOutputBuffer(); } void getInfo(string infoFileName, vector<FrameInfo> &Info) { ifstream eleInfo; eleInfo.open(infoFileName); ASSERT_EQ(eleInfo.is_open(), true) << "Failed to open " << infoFileName; int32_t bytesCount = 0; uint32_t flags = 0; uint32_t timestamp = 0; while (1) { if (!(eleInfo >> bytesCount)) { break; } eleInfo >> flags; eleInfo >> timestamp; Info.push_back({bytesCount, flags, timestamp}); } if (eleInfo.is_open()) eleInfo.close(); } TEST_P(Mpeg4H263DecoderTest, DecodeTest) { tuple<string /* InputFileName */, string /* InfoFileName */, bool /* mode */> params = GetParam(); string inputFileName = gEnv->getRes() + get<0>(params); mEleStream.open(inputFileName, ifstream::binary); ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << get<0>(params); string infoFileName = gEnv->getRes() + get<1>(params); vector<FrameInfo> Info; ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info)); ASSERT_NE(Info.empty(), true) << "Invalid Info file"; ofstream ostrm; ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary); ASSERT_EQ(ostrm.is_open(), true) << "Failed to open output stream for " << get<0>(params); status_t err = initDecoder(); ASSERT_EQ(err, OK) << "initDecoder: failed to create decoder " << err; bool isMpeg4 = get<2>(params); MP4DecodingMode inputMode = isMpeg4 ? MPEG4_MODE : H263_MODE; ASSERT_NO_FATAL_FAILURE( processMpeg4H263Decoder(Info, 0, Info.size(), mEleStream, ostrm, inputMode)); deInitDecoder(); ostrm.close(); Info.clear(); } TEST_P(Mpeg4H263DecoderTest, FlushTest) { tuple<string /* InputFileName */, string /* InfoFileName */, bool /* mode */> params = GetParam(); string inputFileName = gEnv->getRes() + get<0>(params); mEleStream.open(inputFileName, ifstream::binary); ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << get<0>(params); string infoFileName = gEnv->getRes() + get<1>(params); vector<FrameInfo> Info; ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info)); ASSERT_NE(Info.empty(), true) << "Invalid Info file"; ofstream ostrm; ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary); ASSERT_EQ(ostrm.is_open(), true) << "Failed to open output stream for " << get<0>(params); status_t err = initDecoder(); ASSERT_EQ(err, OK) << "initDecoder: failed to create decoder " << err; bool isMpeg4 = get<2>(params); MP4DecodingMode inputMode = isMpeg4 ? MPEG4_MODE : H263_MODE; // Number of frames to be decoded before flush int32_t numFrames = Info.size() / 3; ASSERT_NO_FATAL_FAILURE( processMpeg4H263Decoder(Info, 0, numFrames, mEleStream, ostrm, inputMode)); if (mInitialized) { int32_t status = PVResetVideoDecoder(mDecHandle); ASSERT_EQ(status, PV_TRUE); } // Seek to next key frame and start decoding till the end int32_t index = numFrames; bool keyFrame = false; uint32_t flags = 0; while (index < (int32_t)Info.size()) { if (Info[index].flags) flags = 1u << (Info[index].flags - 1); if ((flags & SYNC_FRAME) == SYNC_FRAME) { keyFrame = true; break; } flags = 0; mEleStream.ignore(Info[index].bytesCount); index++; } ALOGV("Index= %d", index); if (keyFrame) { mNumSamplesOutput = 0; ASSERT_NO_FATAL_FAILURE(processMpeg4H263Decoder(Info, index, (int32_t)Info.size() - index, mEleStream, ostrm, inputMode)); } deInitDecoder(); ostrm.close(); Info.clear(); } INSTANTIATE_TEST_SUITE_P( Mpeg4H263DecoderTestAll, Mpeg4H263DecoderTest, ::testing::Values(make_tuple("swirl_128x96_h263.h263", "swirl_128x96_h263.info", false), make_tuple("swirl_176x144_h263.h263", "swirl_176x144_h263.info", false), make_tuple("swirl_352x288_h263.h263", "swirl_352x288_h263.info", false), make_tuple("bbb_352x288_h263.h263", "bbb_352x288_h263.info", false), make_tuple("bbb_352x288_mpeg4.m4v", "bbb_352x288_mpeg4.info", true), make_tuple("swirl_128x128_mpeg4.m4v", "swirl_128x128_mpeg4.info", true), make_tuple("swirl_130x132_mpeg4.m4v", "swirl_130x132_mpeg4.info", true), make_tuple("swirl_132x130_mpeg4.m4v", "swirl_132x130_mpeg4.info", true), make_tuple("swirl_136x144_mpeg4.m4v", "swirl_136x144_mpeg4.info", true), make_tuple("swirl_144x136_mpeg4.m4v", "swirl_144x136_mpeg4.info", true))); int main(int argc, char **argv) { gEnv = new Mpeg4H263DecoderTestEnvironment(); ::testing::AddGlobalTestEnvironment(gEnv); ::testing::InitGoogleTest(&argc, argv); int status = gEnv->initFromOptions(argc, argv); if (status == 0) { status = RUN_ALL_TESTS(); ALOGD("Decoder Test Result = %d\n", status); } return status; } media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTestEnvironment.h 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * 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_DECODER_TEST_ENVIRONMENT_H__ #define __MPEG4_H263_DECODER_TEST_ENVIRONMENT_H__ #include <gtest/gtest.h> #include <getopt.h> using namespace std; class Mpeg4H263DecoderTestEnvironment : public ::testing::Environment { public: Mpeg4H263DecoderTestEnvironment() : res("/data/local/tmp/") {} // 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 Mpeg4H263DecoderTestEnvironment::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_DECODER_TEST_ENVIRONMENT_H__ media/libstagefright/codecs/m4v_h263/dec/test/README.md 0 → 100644 +34 −0 Original line number Diff line number Diff line ## Media Testing ## --- #### Mpeg4H263Decoder : The Mpeg4H263Decoder Test Suite validates the Mpeg4 and H263 decoder available in libstagefright. Run the following steps to build the test suite: ``` m Mpeg4H263DecoderTest ``` 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/Mpeg4H263DecoderTest/Mpeg4H263DecoderTest /data/local/tmp/ ``` To test 32-bit binary push binaries from nativetest. ``` adb push ${OUT}/data/nativetest/Mpeg4H263DecoderTest/Mpeg4H263DecoderTest /data/local/tmp/ ``` The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Download Mpeg4H263Decoder folder and push all the files in this folder to /data/local/tmp/ on the device for testing. ``` adb push Mpeg4H263Decoder/. /data/local/tmp/ ``` usage: Mpeg4H263DecoderTest -P \<path_to_folder\> ``` adb shell /data/local/tmp/Mpeg4H263DecoderTest -P /data/local/tmp/ ``` Loading
media/libstagefright/codecs/m4v_h263/dec/test/Android.bp 0 → 100644 +47 −0 Original line number Diff line number Diff line /* * 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: "Mpeg4H263DecoderTest", gtest: true, srcs: [ "Mpeg4H263DecoderTest.cpp", ], shared_libs: [ "liblog", ], static_libs: [ "libstagefright_m4vh263dec", "libstagefright_foundation", ], cflags: [ "-DOSCL_IMPORT_REF=", "-Werror", "-Wall", ], sanitize: { misc_undefined: [ "unsigned-integer-overflow", "signed-integer-overflow", ], cfi: true, }, }
media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTest.cpp 0 → 100644 +423 −0 Original line number Diff line number Diff line /* * 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 "Mpeg4H263DecoderTest" #include <utils/Log.h> #include <stdio.h> #include <string.h> #include <utils/String8.h> #include <fstream> #include <media/stagefright/foundation/AUtils.h> #include "mp4dec_api.h" #include "Mpeg4H263DecoderTestEnvironment.h" using namespace android; #define OUTPUT_FILE_NAME "/data/local/tmp/Output.yuv" #define CODEC_CONFIG_FLAG 32 #define SYNC_FRAME 1 #define MPEG4_MAX_WIDTH 1920 #define MPEG4_MAX_HEIGHT 1080 #define H263_MAX_WIDTH 352 #define H263_MAX_HEIGHT 288 constexpr uint32_t kNumOutputBuffers = 2; struct FrameInfo { int32_t bytesCount; uint32_t flags; int64_t timestamp; }; struct tagvideoDecControls; static Mpeg4H263DecoderTestEnvironment *gEnv = nullptr; class Mpeg4H263DecoderTest : public ::testing::TestWithParam<tuple<string, string, bool>> { public: Mpeg4H263DecoderTest() : mDecHandle(nullptr), mInputBuffer(nullptr), mInitialized(false), mFramesConfigured(false), mNumSamplesOutput(0), mWidth(352), mHeight(288) { memset(mOutputBuffer, 0x0, sizeof(mOutputBuffer)); } ~Mpeg4H263DecoderTest() { if (mEleStream.is_open()) mEleStream.close(); if (mDecHandle) { delete mDecHandle; mDecHandle = nullptr; } if (mInputBuffer) { free(mInputBuffer); mInputBuffer = nullptr; } freeOutputBuffer(); } status_t initDecoder(); void allocOutputBuffer(size_t outputBufferSize); void dumpOutput(ofstream &ostrm); void freeOutputBuffer(); void processMpeg4H263Decoder(vector<FrameInfo> Info, int32_t offset, int32_t range, ifstream &mEleStream, ofstream &ostrm, MP4DecodingMode inputMode); void deInitDecoder(); ifstream mEleStream; tagvideoDecControls *mDecHandle; char *mInputBuffer; uint8_t *mOutputBuffer[kNumOutputBuffers]; bool mInitialized; bool mFramesConfigured; uint32_t mNumSamplesOutput; uint32_t mWidth; uint32_t mHeight; }; status_t Mpeg4H263DecoderTest::initDecoder() { if (!mDecHandle) { mDecHandle = new tagvideoDecControls; } if (!mDecHandle) { return NO_MEMORY; } memset(mDecHandle, 0, sizeof(tagvideoDecControls)); return OK; } void Mpeg4H263DecoderTest::allocOutputBuffer(size_t outputBufferSize) { for (int32_t i = 0; i < kNumOutputBuffers; ++i) { if (!mOutputBuffer[i]) { mOutputBuffer[i] = (uint8_t *)malloc(outputBufferSize); ASSERT_NE(mOutputBuffer[i], nullptr) << "Output buffer allocation failed"; } } } void Mpeg4H263DecoderTest::dumpOutput(ofstream &ostrm) { uint8_t *src = mOutputBuffer[mNumSamplesOutput & 1]; size_t vStride = align(mHeight, 16); size_t srcYStride = align(mWidth, 16); size_t srcUVStride = srcYStride / 2; uint8_t *srcStart = src; /* Y buffer */ for (size_t i = 0; i < mHeight; ++i) { ostrm.write(reinterpret_cast<char *>(src), mWidth); src += srcYStride; } /* U buffer */ src = srcStart + vStride * srcYStride; for (size_t i = 0; i < mHeight / 2; ++i) { ostrm.write(reinterpret_cast<char *>(src), mWidth / 2); src += srcUVStride; } /* V buffer */ src = srcStart + vStride * srcYStride * 5 / 4; for (size_t i = 0; i < mHeight / 2; ++i) { ostrm.write(reinterpret_cast<char *>(src), mWidth / 2); src += srcUVStride; } } void Mpeg4H263DecoderTest::freeOutputBuffer() { for (int32_t i = 0; i < kNumOutputBuffers; ++i) { if (mOutputBuffer[i]) { free(mOutputBuffer[i]); mOutputBuffer[i] = nullptr; } } } void Mpeg4H263DecoderTest::processMpeg4H263Decoder(vector<FrameInfo> Info, int32_t offset, int32_t range, ifstream &mEleStream, ofstream &ostrm, MP4DecodingMode inputMode) { size_t maxWidth = (inputMode == MPEG4_MODE) ? MPEG4_MAX_WIDTH : H263_MAX_WIDTH; size_t maxHeight = (inputMode == MPEG4_MODE) ? MPEG4_MAX_HEIGHT : H263_MAX_HEIGHT; size_t outputBufferSize = align(maxWidth, 16) * align(maxHeight, 16) * 3 / 2; uint32_t frameIndex = offset; bool status = true; ASSERT_GE(range, 0) << "Invalid range"; ASSERT_TRUE(offset >= 0 && offset <= Info.size() - 1) << "Invalid offset"; ASSERT_LE(range + offset, Info.size()) << "range+offset can't be greater than the no of frames"; while (1) { if (frameIndex == Info.size() || frameIndex == (offset + range)) break; int32_t bytesCount = Info[frameIndex].bytesCount; ASSERT_GT(bytesCount, 0) << "Size for the memory allocation is negative"; mInputBuffer = (char *)malloc(bytesCount); ASSERT_NE(mInputBuffer, nullptr) << "Insufficient memory to read frame"; mEleStream.read(mInputBuffer, bytesCount); ASSERT_EQ(mEleStream.gcount(), bytesCount) << "mEleStream.gcount() != bytesCount"; static const uint8_t volInfo[] = {0x00, 0x00, 0x01, 0xB0}; bool volHeader = memcmp(mInputBuffer, volInfo, 4) == 0; if (volHeader) { PVCleanUpVideoDecoder(mDecHandle); mInitialized = false; } if (!mInitialized) { uint8_t *volData[1]{}; int32_t volSize = 0; uint32_t flags = Info[frameIndex].flags; bool codecConfig = flags == CODEC_CONFIG_FLAG; if (codecConfig || volHeader) { volData[0] = reinterpret_cast<uint8_t *>(mInputBuffer); volSize = bytesCount; } status = PVInitVideoDecoder(mDecHandle, volData, &volSize, 1, maxWidth, maxHeight, inputMode); ASSERT_TRUE(status) << "PVInitVideoDecoder failed. Unsupported content"; mInitialized = true; MP4DecodingMode actualMode = PVGetDecBitstreamMode(mDecHandle); ASSERT_EQ(inputMode, actualMode) << "Decoded mode not same as actual mode of the decoder"; PVSetPostProcType(mDecHandle, 0); int32_t dispWidth, dispHeight; PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight); int32_t bufWidth, bufHeight; PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight); ASSERT_LE(dispWidth, bufWidth) << "Display width is greater than buffer width"; ASSERT_LE(dispHeight, bufHeight) << "Display height is greater than buffer height"; if (dispWidth != mWidth || dispHeight != mHeight) { mWidth = dispWidth; mHeight = dispHeight; freeOutputBuffer(); if (inputMode == H263_MODE) { PVCleanUpVideoDecoder(mDecHandle); uint8_t *volData[1]{}; int32_t volSize = 0; status = PVInitVideoDecoder(mDecHandle, volData, &volSize, 1, maxWidth, maxHeight, H263_MODE); ASSERT_TRUE(status) << "PVInitVideoDecoder failed for H263"; } mFramesConfigured = false; } if (codecConfig) { frameIndex++; continue; } } uint32_t yFrameSize = sizeof(uint8) * mDecHandle->size; ASSERT_GE(outputBufferSize, yFrameSize * 3 / 2) << "Too small output buffer: " << outputBufferSize << " bytes"; ASSERT_NO_FATAL_FAILURE(allocOutputBuffer(outputBufferSize)); if (!mFramesConfigured) { PVSetReferenceYUV(mDecHandle, mOutputBuffer[1]); mFramesConfigured = true; } // Need to check if header contains new info, e.g., width/height, etc. VopHeaderInfo headerInfo; uint32_t useExtTimestamp = 1; int32_t inputSize = (Info)[frameIndex].bytesCount; uint32_t timestamp = frameIndex; uint8_t *bitstreamTmp = reinterpret_cast<uint8_t *>(mInputBuffer); status = PVDecodeVopHeader(mDecHandle, &bitstreamTmp, ×tamp, &inputSize, &headerInfo, &useExtTimestamp, mOutputBuffer[mNumSamplesOutput & 1]); ASSERT_EQ(status, PV_TRUE) << "failed to decode vop header"; // H263 doesn't have VOL header, the frame size information is in short header, i.e. the // decoder may detect size change after PVDecodeVopHeader. int32_t dispWidth, dispHeight; PVGetVideoDimensions(mDecHandle, &dispWidth, &dispHeight); int32_t bufWidth, bufHeight; PVGetBufferDimensions(mDecHandle, &bufWidth, &bufHeight); ASSERT_LE(dispWidth, bufWidth) << "Display width is greater than buffer width"; ASSERT_LE(dispHeight, bufHeight) << "Display height is greater than buffer height"; if (dispWidth != mWidth || dispHeight != mHeight) { mWidth = dispWidth; mHeight = dispHeight; } status = PVDecodeVopBody(mDecHandle, &inputSize); ASSERT_EQ(status, PV_TRUE) << "failed to decode video frame No = %d" << frameIndex; dumpOutput(ostrm); ++mNumSamplesOutput; ++frameIndex; } freeOutputBuffer(); } void Mpeg4H263DecoderTest::deInitDecoder() { if (mInitialized) { if (mDecHandle) { PVCleanUpVideoDecoder(mDecHandle); delete mDecHandle; mDecHandle = nullptr; } mInitialized = false; } freeOutputBuffer(); } void getInfo(string infoFileName, vector<FrameInfo> &Info) { ifstream eleInfo; eleInfo.open(infoFileName); ASSERT_EQ(eleInfo.is_open(), true) << "Failed to open " << infoFileName; int32_t bytesCount = 0; uint32_t flags = 0; uint32_t timestamp = 0; while (1) { if (!(eleInfo >> bytesCount)) { break; } eleInfo >> flags; eleInfo >> timestamp; Info.push_back({bytesCount, flags, timestamp}); } if (eleInfo.is_open()) eleInfo.close(); } TEST_P(Mpeg4H263DecoderTest, DecodeTest) { tuple<string /* InputFileName */, string /* InfoFileName */, bool /* mode */> params = GetParam(); string inputFileName = gEnv->getRes() + get<0>(params); mEleStream.open(inputFileName, ifstream::binary); ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << get<0>(params); string infoFileName = gEnv->getRes() + get<1>(params); vector<FrameInfo> Info; ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info)); ASSERT_NE(Info.empty(), true) << "Invalid Info file"; ofstream ostrm; ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary); ASSERT_EQ(ostrm.is_open(), true) << "Failed to open output stream for " << get<0>(params); status_t err = initDecoder(); ASSERT_EQ(err, OK) << "initDecoder: failed to create decoder " << err; bool isMpeg4 = get<2>(params); MP4DecodingMode inputMode = isMpeg4 ? MPEG4_MODE : H263_MODE; ASSERT_NO_FATAL_FAILURE( processMpeg4H263Decoder(Info, 0, Info.size(), mEleStream, ostrm, inputMode)); deInitDecoder(); ostrm.close(); Info.clear(); } TEST_P(Mpeg4H263DecoderTest, FlushTest) { tuple<string /* InputFileName */, string /* InfoFileName */, bool /* mode */> params = GetParam(); string inputFileName = gEnv->getRes() + get<0>(params); mEleStream.open(inputFileName, ifstream::binary); ASSERT_EQ(mEleStream.is_open(), true) << "Failed to open " << get<0>(params); string infoFileName = gEnv->getRes() + get<1>(params); vector<FrameInfo> Info; ASSERT_NO_FATAL_FAILURE(getInfo(infoFileName, Info)); ASSERT_NE(Info.empty(), true) << "Invalid Info file"; ofstream ostrm; ostrm.open(OUTPUT_FILE_NAME, std::ofstream::binary); ASSERT_EQ(ostrm.is_open(), true) << "Failed to open output stream for " << get<0>(params); status_t err = initDecoder(); ASSERT_EQ(err, OK) << "initDecoder: failed to create decoder " << err; bool isMpeg4 = get<2>(params); MP4DecodingMode inputMode = isMpeg4 ? MPEG4_MODE : H263_MODE; // Number of frames to be decoded before flush int32_t numFrames = Info.size() / 3; ASSERT_NO_FATAL_FAILURE( processMpeg4H263Decoder(Info, 0, numFrames, mEleStream, ostrm, inputMode)); if (mInitialized) { int32_t status = PVResetVideoDecoder(mDecHandle); ASSERT_EQ(status, PV_TRUE); } // Seek to next key frame and start decoding till the end int32_t index = numFrames; bool keyFrame = false; uint32_t flags = 0; while (index < (int32_t)Info.size()) { if (Info[index].flags) flags = 1u << (Info[index].flags - 1); if ((flags & SYNC_FRAME) == SYNC_FRAME) { keyFrame = true; break; } flags = 0; mEleStream.ignore(Info[index].bytesCount); index++; } ALOGV("Index= %d", index); if (keyFrame) { mNumSamplesOutput = 0; ASSERT_NO_FATAL_FAILURE(processMpeg4H263Decoder(Info, index, (int32_t)Info.size() - index, mEleStream, ostrm, inputMode)); } deInitDecoder(); ostrm.close(); Info.clear(); } INSTANTIATE_TEST_SUITE_P( Mpeg4H263DecoderTestAll, Mpeg4H263DecoderTest, ::testing::Values(make_tuple("swirl_128x96_h263.h263", "swirl_128x96_h263.info", false), make_tuple("swirl_176x144_h263.h263", "swirl_176x144_h263.info", false), make_tuple("swirl_352x288_h263.h263", "swirl_352x288_h263.info", false), make_tuple("bbb_352x288_h263.h263", "bbb_352x288_h263.info", false), make_tuple("bbb_352x288_mpeg4.m4v", "bbb_352x288_mpeg4.info", true), make_tuple("swirl_128x128_mpeg4.m4v", "swirl_128x128_mpeg4.info", true), make_tuple("swirl_130x132_mpeg4.m4v", "swirl_130x132_mpeg4.info", true), make_tuple("swirl_132x130_mpeg4.m4v", "swirl_132x130_mpeg4.info", true), make_tuple("swirl_136x144_mpeg4.m4v", "swirl_136x144_mpeg4.info", true), make_tuple("swirl_144x136_mpeg4.m4v", "swirl_144x136_mpeg4.info", true))); int main(int argc, char **argv) { gEnv = new Mpeg4H263DecoderTestEnvironment(); ::testing::AddGlobalTestEnvironment(gEnv); ::testing::InitGoogleTest(&argc, argv); int status = gEnv->initFromOptions(argc, argv); if (status == 0) { status = RUN_ALL_TESTS(); ALOGD("Decoder Test Result = %d\n", status); } return status; }
media/libstagefright/codecs/m4v_h263/dec/test/Mpeg4H263DecoderTestEnvironment.h 0 → 100644 +73 −0 Original line number Diff line number Diff line /* * 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_DECODER_TEST_ENVIRONMENT_H__ #define __MPEG4_H263_DECODER_TEST_ENVIRONMENT_H__ #include <gtest/gtest.h> #include <getopt.h> using namespace std; class Mpeg4H263DecoderTestEnvironment : public ::testing::Environment { public: Mpeg4H263DecoderTestEnvironment() : res("/data/local/tmp/") {} // 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 Mpeg4H263DecoderTestEnvironment::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_DECODER_TEST_ENVIRONMENT_H__
media/libstagefright/codecs/m4v_h263/dec/test/README.md 0 → 100644 +34 −0 Original line number Diff line number Diff line ## Media Testing ## --- #### Mpeg4H263Decoder : The Mpeg4H263Decoder Test Suite validates the Mpeg4 and H263 decoder available in libstagefright. Run the following steps to build the test suite: ``` m Mpeg4H263DecoderTest ``` 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/Mpeg4H263DecoderTest/Mpeg4H263DecoderTest /data/local/tmp/ ``` To test 32-bit binary push binaries from nativetest. ``` adb push ${OUT}/data/nativetest/Mpeg4H263DecoderTest/Mpeg4H263DecoderTest /data/local/tmp/ ``` The resource file for the tests is taken from [here](https://drive.google.com/drive/folders/13cM4tAaVFrmr-zGFqaAzFBbKs75pnm9b). Download Mpeg4H263Decoder folder and push all the files in this folder to /data/local/tmp/ on the device for testing. ``` adb push Mpeg4H263Decoder/. /data/local/tmp/ ``` usage: Mpeg4H263DecoderTest -P \<path_to_folder\> ``` adb shell /data/local/tmp/Mpeg4H263DecoderTest -P /data/local/tmp/ ```