From dcdce6e670c55c62b799090a32cdb2f4ea55cd29 Mon Sep 17 00:00:00 2001 From: "Wan, Hao" Date: Wed, 15 Jan 2025 11:00:20 +0000 Subject: [PATCH] Debug through some Encoder cases and remove unnecessary codes. Signed-off-by: Wan, Hao --- c2_utils/include/mfx_c2_params.h | 75 -- unittests/include/test_streams.h | 2 +- unittests/src/c2_decoder_test.cpp | 12 +- unittests/src/c2_encoder_test.cpp | 387 +++--- unittests/src/c2_mock_component_test.cpp | 723 ------------ unittests/src/c2_utils_test.cpp | 1377 ---------------------- unittests/src/c2_vndk_test.cpp | 533 --------- 7 files changed, 233 insertions(+), 2876 deletions(-) delete mode 100755 c2_utils/include/mfx_c2_params.h delete mode 100755 unittests/src/c2_mock_component_test.cpp delete mode 100755 unittests/src/c2_utils_test.cpp delete mode 100755 unittests/src/c2_vndk_test.cpp diff --git a/c2_utils/include/mfx_c2_params.h b/c2_utils/include/mfx_c2_params.h deleted file mode 100755 index 8a8ac809..00000000 --- a/c2_utils/include/mfx_c2_params.h +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright (c) 2017-2021 Intel Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#pragma once - -#include - -enum C2ParamIndexKindVendor : C2Param::type_index_t { - - kParamIndexRateControl = C2Param::TYPE_INDEX_VENDOR_START, - kParamIndexProfile, - kParamIndexLevel, - kParamIndexFrameQP, - kParamIndexMemoryType, -}; - -// not existing tuning, Google defines bitrate as Info, -// so define bitrate tuning here for test purpose, -// otherwise cannot push_back it into C2Worklet::tunings -typedef C2StreamParam C2BitrateTuning; -constexpr char MFX_C2_PARAMKEY_BITRATE_TUNING[] = "coded.bitrate.tuning"; - -C2ENUM(C2RateControlMethod, int32_t, - C2RateControlCQP, - C2RateControlCBR, - C2RateControlVBR, -); - -typedef C2PortParam, kParamIndexRateControl>::output - C2RateControlSetting; - -struct C2FrameQPStruct { - uint32_t qp_i; - uint32_t qp_p; - uint32_t qp_b; - - DEFINE_AND_DESCRIBE_C2STRUCT(FrameQP) - C2FIELD(qp_i, "QPI") - C2FIELD(qp_p, "QPP") - C2FIELD(qp_b, "QPB") -}; - -typedef C2PortParam::output C2FrameQPSetting; - -typedef C2PortParam::output C2IntraRefreshTuning; - -typedef C2PortParam::output C2ProfileSetting; - -typedef C2PortParam::output C2LevelSetting; - -typedef C2PortParam, kParamIndexProfileLevel> C2ProfileLevelInfo; - -C2ENUM(C2MemoryType, int32_t, - C2MemoryTypeSystem, - C2MemoryTypeGraphics, -); - -typedef C2GlobalParam, kParamIndexMemoryType> C2MemoryTypeSetting; diff --git a/unittests/include/test_streams.h b/unittests/include/test_streams.h index 810b1273..e9127f01 100755 --- a/unittests/include/test_streams.h +++ b/unittests/include/test_streams.h @@ -21,7 +21,7 @@ #pragma once #include -#include "mfx_c2_params.h" +#include #include "mfxstructures.h" struct StreamDescription diff --git a/unittests/src/c2_decoder_test.cpp b/unittests/src/c2_decoder_test.cpp index 5990fbb4..0882c0df 100755 --- a/unittests/src/c2_decoder_test.cpp +++ b/unittests/src/c2_decoder_test.cpp @@ -1094,17 +1094,7 @@ static void Decode( c2_blocking_t may_block{C2_MAY_BLOCK}; component->setListener_vb(validator, may_block); - C2MemoryTypeSetting setting; - setting.value = graphics_memory ? C2MemoryTypeGraphics : C2MemoryTypeSystem; - - std::vector params = { &setting }; - std::vector> failures; - std::shared_ptr comp_intf = component->intf(); - - c2_status_t sts = comp_intf->config_vb(params, may_block, &failures); - EXPECT_EQ(sts, C2_OK); - - sts = component->start(); + c2_status_t sts = component->start(); EXPECT_EQ(sts, C2_OK); uint32_t frame_index = 0; diff --git a/unittests/src/c2_encoder_test.cpp b/unittests/src/c2_encoder_test.cpp index acab8de0..4835e622 100755 --- a/unittests/src/c2_encoder_test.cpp +++ b/unittests/src/c2_encoder_test.cpp @@ -20,12 +20,12 @@ #include "mfx_c2_defs.h" #include +#include #include "test_components.h" #include "test_streams.h" #include "test_params.h" #include "mfx_c2_utils.h" #include "mfx_defaults.h" -#include "mfx_c2_params.h" #include "mfx_c2_component.h" #include "mfx_c2_components_registry.h" #include "C2PlatformSupport.h" @@ -37,46 +37,93 @@ using namespace android; +const unsigned int SINGLE_STREAM_ID = 0u; const float FRAME_RATE = 30.0; // 30 fps const uint64_t FRAME_DURATION_US = (uint64_t)(1000000 / FRAME_RATE); // Low res is chosen to speed up the tests. +const uint32_t MIN_W = 176; +const uint32_t MIN_H = 144; +const uint32_t MAX_W = 4096; +const uint32_t MAX_H = 4096; const uint32_t FRAME_WIDTH = 320; const uint32_t FRAME_HEIGHT = 240; -const uint32_t FRAME_FORMAT = HAL_PIXEL_FORMAT_NV12_TILED_INTEL; // nv12 +const uint32_t FRAME_FORMAT = MFX_FOURCC_NV12; // fourcc nv12 // This frame count is required by StaticBitrate test, the encoder cannot follow // bitrate on shorter frame sequences. const uint32_t FRAME_COUNT = 150; // 10 default GOP size const c2_nsecs_t TIMEOUT_NS = MFX_SECOND_NS; -std::vector DefaultC2Params() -{ - std::vector param = - { - { false, "RateControl", C2RateControlSetting::PARAM_TYPE }, - { false, "FrameRate", C2StreamFrameRateInfo::output::PARAM_TYPE }, - { false, C2_PARAMKEY_BITRATE, C2StreamBitrateInfo::output::PARAM_TYPE }, - { false, MFX_C2_PARAMKEY_BITRATE_TUNING, C2BitrateTuning::output::PARAM_TYPE }, - { false, "FrameQP", C2FrameQPSetting::PARAM_TYPE }, - { false, "IntraRefresh", C2IntraRefreshTuning::PARAM_TYPE }, - { false, "Profile", C2ProfileSetting::PARAM_TYPE }, - { false, "Level", C2LevelSetting::PARAM_TYPE }, - { false, "SupportedProfilesLevels", C2ProfileLevelInfo::output::PARAM_TYPE }, - { false, "MemoryType", C2MemoryTypeSetting::PARAM_TYPE }, - { false, C2_PARAMKEY_COMPONENT_DOMAIN, C2ComponentDomainSetting::PARAM_TYPE }, - { false, C2_PARAMKEY_COMPONENT_KIND, C2ComponentKindSetting::PARAM_TYPE }, - { false, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::input::PARAM_TYPE }, - { false, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::output::PARAM_TYPE }, - { false, C2_PARAMKEY_INPUT_MEDIA_TYPE, C2PortMediaTypeSetting::input::PARAM_TYPE }, - { false, C2_PARAMKEY_OUTPUT_MEDIA_TYPE, C2PortMediaTypeSetting::output::PARAM_TYPE }, - { false, C2_PARAMKEY_PICTURE_SIZE, C2StreamPictureSizeInfo::input::PARAM_TYPE }, - }; - return param; -} +///Deprecated!!! +// std::vector DefaultC2Params() +// { +// std::vector param = +// { +// { false, "RateControl", C2RateControlSetting::PARAM_TYPE }, +// { false, "FrameRate", C2StreamFrameRateInfo::output::PARAM_TYPE }, +// { false, C2_PARAMKEY_BITRATE, C2StreamBitrateInfo::output::PARAM_TYPE }, +// { false, MFX_C2_PARAMKEY_BITRATE_TUNING, C2BitrateTuning::output::PARAM_TYPE }, +// { false, "FrameQP", C2FrameQPSetting::PARAM_TYPE }, +// { false, "IntraRefresh", C2IntraRefreshTuning::PARAM_TYPE }, +// { false, "Profile", C2ProfileSetting::PARAM_TYPE }, +// { false, "Level", C2LevelSetting::PARAM_TYPE }, +// { false, "SupportedProfilesLevels", C2ProfileLevelInfo::output::PARAM_TYPE }, +// { false, "MemoryType", C2MemoryTypeSetting::PARAM_TYPE }, +// { false, C2_PARAMKEY_COMPONENT_DOMAIN, C2ComponentDomainSetting::PARAM_TYPE }, +// { false, C2_PARAMKEY_COMPONENT_KIND, C2ComponentKindSetting::PARAM_TYPE }, +// { false, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::input::PARAM_TYPE }, +// { false, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::output::PARAM_TYPE }, +// { false, C2_PARAMKEY_INPUT_MEDIA_TYPE, C2PortMediaTypeSetting::input::PARAM_TYPE }, +// { false, C2_PARAMKEY_OUTPUT_MEDIA_TYPE, C2PortMediaTypeSetting::output::PARAM_TYPE }, +// { false, C2_PARAMKEY_PICTURE_SIZE, C2StreamPictureSizeInfo::input::PARAM_TYPE }, +// }; +// return param; +// } -static std::vector h264_params_desc = DefaultC2Params(); +static std::vector h264_params_desc = +{ + { false, C2_PARAMKEY_COMPONENT_NAME, C2ComponentNameSetting::PARAM_TYPE }, + { false, C2_PARAMKEY_COMPONENT_KIND, C2ComponentKindSetting::PARAM_TYPE }, + { false, C2_PARAMKEY_COMPONENT_DOMAIN, C2ComponentDomainSetting::PARAM_TYPE }, + { false, C2_PARAMKEY_PICTURE_SIZE, C2StreamPictureSizeInfo::input::PARAM_TYPE }, + { false, C2_PARAMKEY_INPUT_MEDIA_TYPE, C2PortMediaTypeSetting::input::PARAM_TYPE }, + { false, C2_PARAMKEY_OUTPUT_MEDIA_TYPE, C2PortMediaTypeSetting::output::PARAM_TYPE }, + { false, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::input::PARAM_TYPE }, + { false, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::output::PARAM_TYPE }, + { false, C2_PARAMKEY_BITRATE_MODE, C2StreamBitrateModeTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_BITRATE, C2StreamBitrateInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_FRAME_RATE, C2StreamFrameRateInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_GOP, C2StreamGopTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_REQUEST_SYNC_FRAME, C2StreamRequestSyncFrameTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_SYNC_FRAME_INTERVAL, C2StreamSyncFrameIntervalTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_PROFILE_LEVEL, C2StreamProfileLevelInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_COLOR_ASPECTS, C2StreamColorAspectsInfo::input::PARAM_TYPE }, + { false, C2_PARAMKEY_VUI_COLOR_ASPECTS, C2StreamColorAspectsInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_INTRA_REFRESH, C2StreamIntraRefreshTuning::output::PARAM_TYPE }, +}; -static std::vector h265_params_desc = DefaultC2Params(); +static std::vector h265_params_desc = +{ + { false, C2_PARAMKEY_COMPONENT_NAME, C2ComponentNameSetting::PARAM_TYPE }, + { false, C2_PARAMKEY_COMPONENT_KIND, C2ComponentKindSetting::PARAM_TYPE }, + { false, C2_PARAMKEY_COMPONENT_DOMAIN, C2ComponentDomainSetting::PARAM_TYPE }, + { false, C2_PARAMKEY_PICTURE_SIZE, C2StreamPictureSizeInfo::input::PARAM_TYPE }, + { false, C2_PARAMKEY_INPUT_MEDIA_TYPE, C2PortMediaTypeSetting::input::PARAM_TYPE }, + { false, C2_PARAMKEY_OUTPUT_MEDIA_TYPE, C2PortMediaTypeSetting::output::PARAM_TYPE }, + { false, C2_PARAMKEY_INPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::input::PARAM_TYPE }, + { false, C2_PARAMKEY_OUTPUT_STREAM_BUFFER_TYPE, C2StreamBufferTypeSetting::output::PARAM_TYPE }, + { false, C2_PARAMKEY_BITRATE_MODE, C2StreamBitrateModeTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_BITRATE, C2StreamBitrateInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_FRAME_RATE, C2StreamFrameRateInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_GOP, C2StreamGopTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_REQUEST_SYNC_FRAME, C2StreamRequestSyncFrameTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_SYNC_FRAME_INTERVAL, C2StreamSyncFrameIntervalTuning::output::PARAM_TYPE }, + { false, C2_PARAMKEY_PROFILE_LEVEL, C2StreamProfileLevelInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_PIXEL_FORMAT, C2StreamPixelFormatInfo::input::PARAM_TYPE }, + { false, C2_PARAMKEY_COLOR_ASPECTS, C2StreamColorAspectsInfo::input::PARAM_TYPE }, + { false, C2_PARAMKEY_VUI_COLOR_ASPECTS, C2StreamColorAspectsInfo::output::PARAM_TYPE }, + { false, C2_PARAMKEY_INTRA_REFRESH, C2StreamIntraRefreshTuning::output::PARAM_TYPE }, +}; namespace { @@ -112,40 +159,67 @@ namespace { }; } -C2RateControlMethod MfxRateControlToC2(mfxU16 rate_control) -{ - C2RateControlMethod res {}; - - switch(rate_control) { - case MFX_RATECONTROL_CBR: - res = C2RateControlCBR; - break; - default: - res = C2RateControlMethod(-1); - break; - } - return res; -} +// C2RateControlMethod MfxRateControlToC2(mfxU16 rate_control) +// { +// C2RateControlMethod res {}; + +// switch(rate_control) { +// case MFX_RATECONTROL_CBR: +// res = C2RateControlCBR; +// break; +// default: +// res = C2RateControlMethod(-1); +// break; +// } +// return res; +// } -static C2ParamValues GetDefaultValues(const char * component_name) +static C2ParamValues GetDefaultParamValues(const char* component_name) { C2ParamValues default_values; - // get default c2 params from mfx default structure - mfxVideoParam video_params {}; + + ///Deprecated!!! + // // get default c2 params from mfx default structure + // mfxVideoParam video_params {}; + // if (!strcmp(component_name, "c2.intel.avc.encoder")) { + // video_params.mfx.CodecId = MFX_CODEC_AVC; + // } else if (!strcmp(component_name, "c2.intel.hevc.encoder")) { + // video_params.mfx.CodecId = MFX_CODEC_HEVC; + // } else { + // video_params.mfx.CodecId = 0; // UNKNOWN + // } + + // mfx_set_defaults_mfxVideoParam_enc(&video_params); + + // default_values.Append(new C2RateControlSetting(MfxRateControlToC2(video_params.mfx.RateControlMethod))); + // default_values.Append(new C2StreamFrameRateInfo::output(0/*stream*/, C2FloatValue((float)video_params.mfx.FrameInfo.FrameRateExtN / video_params.mfx.FrameInfo.FrameRateExtD))); + // default_values.Append(new C2StreamBitrateInfo::output(0/*stream*/, video_params.mfx.TargetKbps * 1000)); // Convert from Kbsp to bps + // default_values.Append(Invalidate(new C2FrameQPSetting())); + + default_values.Append(new C2StreamPictureSizeInfo::input(SINGLE_STREAM_ID, MIN_W, MIN_H)); + default_values.Append(new C2StreamBitrateModeTuning::output(SINGLE_STREAM_ID, C2Config::BITRATE_VARIABLE)); + default_values.Append(new C2StreamBitrateInfo::output(SINGLE_STREAM_ID, 64000)); + default_values.Append(new C2StreamFrameRateInfo::output(SINGLE_STREAM_ID, 1.)); + default_values.AppendFlex(C2StreamGopTuning::output::AllocUnique(0/* flexCount */, SINGLE_STREAM_ID)); + default_values.Append(new C2StreamRequestSyncFrameTuning::output(SINGLE_STREAM_ID, C2_FALSE)); + default_values.Append(new C2StreamSyncFrameIntervalTuning::output(SINGLE_STREAM_ID, 1000000)); + default_values.Append(new C2StreamColorAspectsInfo::input(SINGLE_STREAM_ID, + C2Color::RANGE_UNSPECIFIED, C2Color::PRIMARIES_UNSPECIFIED, + C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED)); + default_values.Append(new C2StreamColorAspectsInfo::output(SINGLE_STREAM_ID, + C2Color::RANGE_LIMITED, C2Color::PRIMARIES_UNSPECIFIED, + C2Color::TRANSFER_UNSPECIFIED, C2Color::MATRIX_UNSPECIFIED)); + default_values.Append(new C2StreamIntraRefreshTuning::output(0u, C2Config::INTRA_REFRESH_DISABLED, 0.)); + if (!strcmp(component_name, "c2.intel.avc.encoder")) { - video_params.mfx.CodecId = MFX_CODEC_AVC; + default_values.Append(new C2StreamProfileLevelInfo::output( + SINGLE_STREAM_ID, PROFILE_AVC_CONSTRAINED_BASELINE, LEVEL_AVC_5_2)); } else if (!strcmp(component_name, "c2.intel.hevc.encoder")) { - video_params.mfx.CodecId = MFX_CODEC_HEVC; - } else { - video_params.mfx.CodecId = 0; // UNKNOWN + default_values.Append(new C2StreamProfileLevelInfo::output( + SINGLE_STREAM_ID, PROFILE_HEVC_MAIN, LEVEL_HEVC_MAIN_6)); + default_values.Append(new C2StreamPixelFormatInfo::input(0u, HAL_PIXEL_FORMAT_YCBCR_420_888)); } - mfx_set_defaults_mfxVideoParam_enc(&video_params); - - default_values.Append(new C2RateControlSetting(MfxRateControlToC2(video_params.mfx.RateControlMethod))); - default_values.Append(new C2StreamFrameRateInfo::output(0/*stream*/, C2FloatValue((float)video_params.mfx.FrameInfo.FrameRateExtN / video_params.mfx.FrameInfo.FrameRateExtD))); - default_values.Append(new C2StreamBitrateInfo::output(0/*stream*/, video_params.mfx.TargetKbps * 1000)); // Convert from Kbsp to bps - default_values.Append(Invalidate(new C2FrameQPSetting())); return default_values; } @@ -158,11 +232,25 @@ static ComponentDesc NonExistingEncoderDesc() } static ComponentDesc g_components_desc[] = { - { "c2.intel.avc.encoder", MfxC2Component::CreateConfig{.concurrent_instances=12,}, C2_OK, h264_params_desc, GetDefaultValues("c2.intel.avc.encoder"), C2_CORRUPTED, - { g_h264_profile_levels, g_h264_profile_levels + g_h264_profile_levels_count }, MFX_CODEC_AVC, + { "c2.intel.avc.encoder", + MfxC2Component::CreateConfig{ + .concurrent_instances=12, }, + C2_OK, + h264_params_desc, + GetDefaultParamValues("c2.intel.avc.encoder"), + /*C2_CORRUPTED*/C2_OK, + { g_h264_profile_levels, g_h264_profile_levels + g_h264_profile_levels_count }, + MFX_CODEC_AVC, &TestAvcStreamProfileLevel }, - { "c2.intel.hevc.encoder", MfxC2Component::CreateConfig{.concurrent_instances=12,}, C2_OK, h265_params_desc, GetDefaultValues("c2.intel.hevc.encoder"), C2_CORRUPTED, - { g_h265_profile_levels, g_h265_profile_levels + g_h265_profile_levels_count }, MFX_CODEC_HEVC, + { "c2.intel.hevc.encoder", + MfxC2Component::CreateConfig{ + .concurrent_instances=12, }, + C2_OK, + h265_params_desc, + GetDefaultParamValues("c2.intel.hevc.encoder"), + /*C2_CORRUPTED*/C2_OK, + { g_h265_profile_levels, g_h265_profile_levels + g_h265_profile_levels_count }, + MFX_CODEC_HEVC, &TestHevcStreamProfileLevel }, }; @@ -213,7 +301,6 @@ static void PrepareWork(uint32_t frame_index, bool last_frame, bool graphics_mem buffer_pack->ordinal.customOrdinal = 0; do { - std::shared_ptr allocator; c2_status_t sts = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &allocator); @@ -227,9 +314,11 @@ static void PrepareWork(uint32_t frame_index, bool last_frame, bool graphics_mem graphics_memory ? android::C2AndroidMemoryUsage::HW_CODEC_READ : C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; + std::shared_ptr block; - sts = allocator->fetchGraphicBlock(FRAME_WIDTH, FRAME_HEIGHT, FRAME_FORMAT, - mem_usage, &block); + sts = allocator->fetchGraphicBlock(FRAME_WIDTH, FRAME_HEIGHT, + MfxFourCCToGralloc(FRAME_FORMAT, graphics_memory), + mem_usage, &block); EXPECT_EQ(sts, C2_OK); EXPECT_NE(block, nullptr); @@ -243,21 +332,25 @@ static void PrepareWork(uint32_t frame_index, bool last_frame, bool graphics_mem EXPECT_TRUE(graph_view); if (graph_view) { - uint8_t* const* data = graph_view->data(); - const C2PlanarLayout layout = graph_view->layout(); + EXPECT_EQ(layout.type, C2PlanarLayout::TYPE_YUV); + + // std::cout << "[wanhao] layout.type: " << layout.type << std::endl; + uint8_t* const* data = graph_view->data(); EXPECT_NE(data, nullptr); + + // std::cout << "[wanhao] layout.numPlanes: " << layout.numPlanes << std::endl; for (uint32_t i = 0; i < layout.numPlanes; ++i) { EXPECT_NE(data[i], nullptr); } - EXPECT_EQ(FRAME_FORMAT, HAL_PIXEL_FORMAT_NV12_TILED_INTEL); - const uint32_t stride = layout.planes[C2PlanarLayout::PLANE_Y].rowInc; const uint32_t alloc_height = (data[C2PlanarLayout::PLANE_U] - data[C2PlanarLayout::PLANE_Y]) / stride; - const size_t frame_size = stride * alloc_height * 3 / 2; + // std::cout << "[wanhao] stride: " << stride << + // " alloc_height: " << alloc_height << " frame_size: " << frame_size << std::endl; + // Allocate frame in system memory, generate contents there, copy to gpu memory // as direct write per pixel is very slow. std::vector frame(frame_size); @@ -328,7 +421,6 @@ class EncoderConsumer : public C2Component::Listener EXPECT_EQ(work->workletsProcessed, 1u) << NAMED(frame_index); EXPECT_EQ(work->result, C2_OK) << NAMED(frame_index); - EXPECT_EQ(buffer_pack.ordinal.timestamp, frame_index * FRAME_DURATION_US); // 30 fps if (buffer_pack.buffers.size() != 0) { @@ -415,17 +507,7 @@ static void Encode( c2_blocking_t may_block {}; component->setListener_vb(validator, may_block); - C2MemoryTypeSetting setting; - setting.value = graphics_memory ? C2MemoryTypeGraphics : C2MemoryTypeSystem; - - std::vector params = { &setting }; - std::vector> failures; - std::shared_ptr comp_intf = component->intf(); - - c2_status_t sts = comp_intf->config_vb(params, may_block, &failures); - EXPECT_EQ(sts, C2_OK); - - sts = component->start(); + c2_status_t sts = component->start(); EXPECT_EQ(sts, C2_OK); for(uint32_t frame_index = 0; frame_index < frame_count; ++frame_index) { @@ -479,15 +561,14 @@ static void Encode( // }; // for(int i = 0; i < TESTS_COUNT; ++i) { - // GTestBinaryWriter writer(std::ostringstream() // << comp_intf->getName() << "-" << i << ".out"); +// std::cout << "[wanhao] " << comp_intf->getName() << "-" << i << ".out" << std::endl; // StripeGenerator stripe_generator; // EncoderConsumer::OnFrame on_frame = // [&] (const C2Worklet&, const uint8_t* data, size_t length) { - // writer.Write(data, length); // binary[i].PushBack(data, length); // }; @@ -497,6 +578,7 @@ static void Encode( // Encode(FRAME_COUNT, use_graphics_memory(i), comp, validator, { &stripe_generator } ); // } + // // Every pair of results should be equal // for (int i = 0; i < TESTS_COUNT - 1; ++i) { // for (int j = i + 1; j < TESTS_COUNT; ++j) { @@ -507,12 +589,12 @@ static void Encode( // } ); // } -// Encodes same stream with diffetent amounts of empty works at the end -// 0 empty works - usual stream, last work eos flagged and contains a buffer -// 1 - last work is empty and eos -// 2 - extra empty work before eos flagged work -// Test checks encoder handling empty works. -// Results should be the same. +// // Encodes same stream with diffetent amounts of empty works at the end +// // 0 empty works - usual stream, last work eos flagged and contains a buffer +// // 1 - last work is empty and eos +// // 2 - extra empty work before eos flagged work +// // Test checks encoder handling empty works. +// // Results should be the same. // TEST_P(Encoder, EncodeEmptyWorks) // { // CallComponentTest(GetParam(), @@ -586,55 +668,49 @@ struct EncoderListener : public C2Component::Listener // While encoding, stops when gets first output, // then starts again. // Decodes till the end on last pass though. -// Despite the stop operation it should normally process all queued works, -// except streams with reordering for those some works should be -// flushed with C2_NOT_FOUND, not C2_CANCELED. -// C2_NOT_FOUND status should be returned as other error statuses are treated -// by libstagefright as fatal. -// TEST_P(Encoder, StopWhileEncoding) -// { -// CallComponentTest(GetParam(), -// [] (const ComponentDesc&, C2CompPtr comp, C2CompIntfPtr) { - -// const int REPEATS_COUNT = 3; -// std::set status_set_; // gather statuses for all passes +TEST_P(Encoder, StopWhileEncoding) +{ + CallComponentTest(GetParam(), + [] (const ComponentDesc&, C2CompPtr comp, C2CompIntfPtr) { -// for (int i = 0; i < REPEATS_COUNT; ++i) { + const int REPEATS_COUNT = 3; + std::set status_set_; // gather statuses for all passes -// std::atomic got_work_{false}; + for (int i = 0; i < REPEATS_COUNT; ++i) { + std::atomic got_work_{false}; -// auto on_work_done = [&](const std::unique_ptr& work) { -// status_set_.insert(work->result); -// got_work_ = true; -// }; + auto on_work_done = [&](const std::unique_ptr& work) { + status_set_.insert(work->result); + got_work_ = true; + }; -// comp->setListener_vb(std::make_shared(on_work_done), C2_MAY_BLOCK); + comp->setListener_vb(std::make_shared(on_work_done), C2_MAY_BLOCK); -// StripeGenerator stripe_generator; + StripeGenerator stripe_generator; -// EXPECT_EQ(comp->start(), C2_OK); + EXPECT_EQ(comp->start(), C2_OK); -// for(uint32_t frame_index = 0; frame_index < FRAME_COUNT; ++frame_index) { + for(uint32_t frame_index = 0; frame_index < FRAME_COUNT; ++frame_index) { -// // if pass is not last stop queueing -// if (i != (REPEATS_COUNT - 1) && got_work_) break; + // if pass is not last stop queueing + if (i != (REPEATS_COUNT - 1) && got_work_) break; -// std::unique_ptr work; -// PrepareWork(frame_index, frame_index == FRAME_COUNT - 1, false/*graphics_memory*/, -// comp, &work, { &stripe_generator }); + std::unique_ptr work; + PrepareWork(frame_index, frame_index == (FRAME_COUNT - 1), false, + comp, &work, { &stripe_generator }); -// std::list> works; -// works.push_back(std::move(work)); -// EXPECT_EQ(comp->queue_nb(&works), C2_OK); -// } + std::list> works; + works.push_back(std::move(work)); + EXPECT_EQ(comp->queue_nb(&works), C2_OK); + } -// EXPECT_EQ(comp->stop(), C2_OK); + EXPECT_EQ(comp->stop(), C2_OK); -// std::set expected_status_set{C2_OK, C2_NOT_FOUND}; -// EXPECT_EQ(status_set_, expected_status_set); -// } -// } ); -// } + std::set expected_status_set{ C2_OK }; + EXPECT_EQ(status_set_, expected_status_set); + } + }); +} // Checks the correctness of all encoding components state machine. // The component should be able to start from STOPPED (initial) state, @@ -660,36 +736,35 @@ TEST_P(Encoder, State) } ); } -// // Checks list of actually supported parameters by all encoding components. -// // Parameters order doesn't matter. -// // For every parameter index, name, required and persistent fields are checked. -// TEST_P(Encoder, getSupportedParams) -// { -// CallComponentTest(GetParam(), -// [] (const ComponentDesc& desc, C2CompPtr, C2CompIntfPtr comp_intf) { - -// std::vector> params_actual; -// c2_status_t sts = comp_intf->querySupportedParams_nb(¶ms_actual); -// EXPECT_EQ(sts, C2_OK); - -// EXPECT_EQ(desc.params_desc.size(), params_actual.size()); - -// for(const C2ParamDescriptor& param_expected : desc.params_desc) { - -// const auto found_actual = std::find_if(params_actual.begin(), params_actual.end(), -// [&] (auto p) { return p->index() == param_expected.index(); } ); +// Checks list of actually supported parameters by all encoding components. +// Parameters order doesn't matter. +// For every parameter index, name, required and persistent fields are checked. +TEST_P(Encoder, getSupportedParams) +{ + CallComponentTest(GetParam(), + [] (const ComponentDesc& desc, C2CompPtr, C2CompIntfPtr comp_intf) { -// EXPECT_NE(found_actual, params_actual.end()) -// << "missing parameter " << param_expected.name(); -// if (found_actual != params_actual.end()) { -// EXPECT_EQ((*found_actual)->isRequired(), param_expected.isRequired()); -// EXPECT_EQ((*found_actual)->isPersistent(), param_expected.isPersistent()); -// EXPECT_EQ((*found_actual)->name(), param_expected.name()); -// } -// } -// } ); -// } + std::vector> params_actual; + c2_status_t sts = comp_intf->querySupportedParams_nb(¶ms_actual); + EXPECT_EQ(sts, C2_OK); + EXPECT_EQ(desc.params_desc.size(), params_actual.size()); + + for(const C2ParamDescriptor& param_expected : desc.params_desc) { + const auto found_actual = std::find_if(params_actual.begin(), params_actual.end(), + [&] (auto p) { return p->index() == param_expected.index(); } ); + + EXPECT_NE(found_actual, params_actual.end()) + << "missing parameter " << param_expected.name(); + if (found_actual != params_actual.end()) { + EXPECT_EQ((*found_actual)->isRequired(), param_expected.isRequired()); + EXPECT_EQ((*found_actual)->isPersistent(), param_expected.isPersistent()); + EXPECT_EQ((*found_actual)->name(), param_expected.name()); + } + } + } ); +} +/// Deprecated!!! // // Tests if all encoding components handle config_vb with not existing parameter correctly. // // It should return individual C2SettingResult failure structure with // // initialized fields and aggregate status value. @@ -701,7 +776,6 @@ TEST_P(Encoder, State) // const uint32_t kParamIndexUnsupported = C2Param::TYPE_INDEX_VENDOR_START + 1000; // typedef C2GlobalParam C2UnsupportedSetting; - // C2UnsupportedSetting setting; // std::vector params = { &setting }; @@ -965,6 +1039,7 @@ TEST_P(Encoder, State) // }); // CallComponentTest // } +///Deprecated!!! // // Queries param values and verify correct defaults. // // Does check before encoding (STOPPED state), during encoding on every frame, // // and after encoding. @@ -1579,7 +1654,7 @@ TEST_P(Encoder, EncodeHeaderSupplied) // [&] (const C2Worklet&, const uint8_t*, size_t) { // std::unique_ptr resolution_param = -// std::make_unique(0/*stream id*/); +// std::make_unique(SINGLE_STREAM_ID); // c2_status_t sts = comp_intf->query_vb({resolution_param.get()}, // {}, C2_MAY_BLOCK, nullptr); @@ -1602,8 +1677,8 @@ static C2ParamValues GetConstParamValues(uint32_t four_cc) const_values.Append(new C2ComponentDomainSetting(C2Component::DOMAIN_VIDEO)); const_values.Append(new C2ComponentKindSetting(C2Component::KIND_ENCODER)); - const_values.Append(new C2StreamBufferTypeSetting::input(0/*stream*/, C2BufferData::GRAPHIC)); - const_values.Append(new C2StreamBufferTypeSetting::output(0/*stream*/, C2BufferData::LINEAR)); + const_values.Append(new C2StreamBufferTypeSetting::input(SINGLE_STREAM_ID, C2BufferData::GRAPHIC)); + const_values.Append(new C2StreamBufferTypeSetting::output(SINGLE_STREAM_ID, C2BufferData::LINEAR)); const_values.AppendFlex(AllocUniqueString("video/raw")); if (MFX_CODEC_AVC == four_cc) diff --git a/unittests/src/c2_mock_component_test.cpp b/unittests/src/c2_mock_component_test.cpp deleted file mode 100755 index 2b291cc3..00000000 --- a/unittests/src/c2_mock_component_test.cpp +++ /dev/null @@ -1,723 +0,0 @@ -// Copyright (c) 2017-2021 Intel Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include -#include -#include -#include - -#include "mfx_c2_defs.h" -#include "mfx_c2_utils.h" -#include -#include "mfx_c2_component.h" -#include "mfx_c2_components_registry.h" -#include "mfx_c2_mock_component.h" -#include "C2PlatformSupport.h" - -#include -#include -#include - -using namespace android; - -#define MOCK_COMPONENT_ENC "c2.intel.mock.encoder" -#define MOCK_COMPONENT_DEC "c2.intel.mock.decoder" -#define MOCK_COMPONENT MOCK_COMPONENT_ENC // use encoder for common tests - -const uint64_t FRAME_DURATION_US = 33333; // 30 fps -const uint32_t FRAME_WIDTH = 640; -const uint32_t FRAME_HEIGHT = 480; -const uint32_t FRAME_BUF_SIZE = FRAME_WIDTH * FRAME_HEIGHT * 3 / 2; - -const uint32_t FRAME_FORMAT = HAL_PIXEL_FORMAT_NV12_TILED_INTEL; // nv12 -const uint32_t FRAME_COUNT = 10; -const c2_nsecs_t TIMEOUT_NS = MFX_SECOND_NS; - -// Tests if the mock component is created OK. -TEST(MfxMockComponent, Create) -{ - MfxC2Component::CreateConfig config{}; - c2_status_t sts = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr mfx_component(MfxCreateC2Component(MOCK_COMPONENT, config, reflector, &sts)); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(mfx_component, nullptr); -} - -// Tests mock component expose C2ComponentInterface -// and return correct information once queried (component name). -TEST(MfxMockComponent, intf) -{ - MfxC2Component::CreateConfig config{}; - c2_status_t sts = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr mfx_component(MfxCreateC2Component(MOCK_COMPONENT, config, reflector, &sts)); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(mfx_component, nullptr); - if(mfx_component != nullptr) { - std::shared_ptr c2_component = mfx_component; - std::shared_ptr c2_component_intf = c2_component->intf(); - - EXPECT_NE(c2_component_intf, nullptr); - if(c2_component_intf != nullptr) { - EXPECT_EQ(c2_component_intf->getName(), MOCK_COMPONENT); - } - } -} - -// Allocates c2 graphic block of FRAME_WIDTH x FRAME_HEIGHT size and fills it with -// specified byte value. -static std::unique_ptr CreateFilledGraphicBlock( - std::shared_ptr allocator, uint8_t fill, uint64_t consumer_memory_type) -{ - std::unique_ptr res; - - do { - C2MemoryUsage mem_usage = { consumer_memory_type, C2MemoryUsage::CPU_WRITE }; - std::shared_ptr block; - c2_status_t sts = allocator->fetchGraphicBlock(FRAME_WIDTH, FRAME_HEIGHT, FRAME_FORMAT, - mem_usage, &block); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(block, nullptr); - - if(nullptr == block) break; - { - std::unique_ptr graph_view; - sts = MapGraphicBlock(*block, TIMEOUT_NS, &graph_view); - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(graph_view, nullptr); - - if(nullptr == graph_view) break; - - memset(graph_view->data()[0], fill, FRAME_BUF_SIZE); - } - // C2Event event; // not supported yet, left for future use - // event.fire(); // pre-fire as buffer is already ready to use - res = std::make_unique(block->share(block->crop(), C2Fence()/*event.fence()*/)); - - } while(false); - - return res; -} - -// Allocates c2 linear block of FRAME_BUF_SIZE length and fills it with -// specified byte value. -static std::unique_ptr CreateFilledLinearBlock( - std::shared_ptr allocator, uint8_t fill) -{ - std::unique_ptr res; - - do { - C2MemoryUsage mem_usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; - std::shared_ptr block; - c2_status_t sts = allocator->fetchLinearBlock(FRAME_BUF_SIZE, mem_usage, &block); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(block, nullptr); - - if(nullptr == block) break; - - std::unique_ptr write_view; - sts = MapLinearBlock(*block, TIMEOUT_NS, &write_view); - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(write_view, nullptr); - - if(nullptr == write_view) break; - - uint8_t* data = write_view->data(); - EXPECT_NE(data, nullptr); - - if(nullptr == data) break; - - memset(data, fill, FRAME_BUF_SIZE); - - // C2Event event; // not supported yet, left for future use - // event.fire(); // pre-fire as buffer is already ready to use - res = std::make_unique(block->share(0, block->capacity(), C2Fence()/*event.fence()*/)); - - } while(false); - - return res; -} - -// Prepares C2Work filling it with NV12 frame. -// Frame size is (FRAME_WIDTH x FRAME_HEIGHT). -// Frame buffer size is (FRAME_WIDTH * FRAME_HEIGHT * 3 / 2). -// Each byte in NV12 frame is set to frame_index. -// Frame header index and timestamp are set based on passed frame_index value. -static void PrepareWork(uint32_t frame_index, - std::shared_ptr component, - std::unique_ptr* work, - C2BufferData::type_t buffer_type, uint64_t consumer_memory_type) -{ - *work = std::make_unique(); - C2FrameData* buffer_pack = &((*work)->input); - - if(frame_index < FRAME_COUNT - 1) { - buffer_pack->flags = C2FrameData::flags_t(0); - } else { - buffer_pack->flags = C2FrameData::FLAG_END_OF_STREAM; - } - - // Set up frame header properties: - // timestamp is set to correspond to 30 fps stream. - buffer_pack->ordinal.timestamp = FRAME_DURATION_US * frame_index; - buffer_pack->ordinal.frameIndex = frame_index; - buffer_pack->ordinal.customOrdinal = 0; - - do { - std::shared_ptr allocator; - auto block_pool_id = (buffer_type == C2BufferData::LINEAR) ? - C2BlockPool::BASIC_LINEAR : C2BlockPool::BASIC_GRAPHIC; - c2_status_t sts = GetCodec2BlockPool(block_pool_id, - component, &allocator); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(allocator, nullptr); - - if(nullptr == allocator) break; - - std::shared_ptr buffer; - // fill the frame with pixels == frame_index - if(buffer_type == C2BufferData::GRAPHIC) { - std::unique_ptr const_block = - CreateFilledGraphicBlock(allocator, (uint8_t)frame_index, consumer_memory_type); - if(nullptr == const_block) break; - // make buffer of graphic block - buffer = std::make_shared(MakeC2Buffer( { *const_block } )); - } else { - std::unique_ptr const_block = CreateFilledLinearBlock(allocator, (uint8_t)frame_index); - if(nullptr == const_block) break; - // make buffer of linear block - buffer = std::make_shared(MakeC2Buffer( { *const_block } )); - } - - buffer_pack->buffers.push_back(buffer); - - std::unique_ptr worklet = std::make_unique(); - // C2 requires output items be allocated in buffers list and set to nulls - worklet->output.buffers.push_back(nullptr); - // work of 1 worklet - (*work)->worklets.push_back(std::move(worklet)); - } - while(false); -} - -static void CheckFilledBuffer(const uint8_t* raw, int expected_item) -{ - if(nullptr != raw) { - - bool match = true; - for(uint32_t i = 0; i < FRAME_BUF_SIZE; ++i) { - // all bytes in buffer should be equal to frame index - if(raw[i] != expected_item) { - match = false; - break; - } - } - EXPECT_EQ(match, true); - } -} - -class MockOutputValidator : public C2Component::Listener -{ -public: - MockOutputValidator(C2BufferData::type_t output_type) - : output_type_(output_type) - { - } - // future ready when validator got all expected frames - std::future GetFuture() - { - return done_.get_future(); - } - -protected: - void onWorkDone_nb( - std::weak_ptr component, - std::list> workItems) override - { - (void)component; - - for(std::unique_ptr& work : workItems) { - EXPECT_EQ(work->workletsProcessed, 1u); - EXPECT_EQ(work->result, C2_OK); - EXPECT_EQ(work->worklets.size(), 1u); - if (work->worklets.size() >= 1) { - - std::unique_ptr& worklet = work->worklets.front(); - C2FrameData& buffer_pack = worklet->output; - - uint64_t frame_index = buffer_pack.ordinal.frameIndex.peeku(); - - bool last_frame = (work->input.flags & C2FrameData::FLAG_END_OF_STREAM) != 0; - EXPECT_EQ(buffer_pack.flags, last_frame ? C2FrameData::FLAG_END_OF_STREAM : C2FrameData::flags_t{}); - EXPECT_EQ(buffer_pack.ordinal.timestamp, frame_index * FRAME_DURATION_US); // 30 fps - - EXPECT_EQ(frame_index < FRAME_COUNT, true) - << "unexpected frame_index value" << frame_index; - { - std::lock_guard lock(expectations_mutex_); - EXPECT_EQ(frame_index, frame_expected_) - << " frame " << frame_index << " is out of order"; - ++frame_expected_; - } - - std::unique_ptr linear_block; - std::unique_ptr graphic_block; - - if(output_type_ == C2BufferData::LINEAR) { - c2_status_t sts = GetC2ConstLinearBlock(buffer_pack, &linear_block); - EXPECT_EQ(sts, C2_OK); - if(nullptr != linear_block) { - EXPECT_EQ(linear_block->capacity(), FRAME_BUF_SIZE); - - std::unique_ptr read_view; - sts = MapConstLinearBlock(*linear_block, TIMEOUT_NS, &read_view); - EXPECT_NE(read_view, nullptr); - - const uint8_t* raw = read_view->data(); - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(raw, nullptr); - - CheckFilledBuffer(raw, frame_index); - } - } else if(output_type_ == C2BufferData::GRAPHIC) { - c2_status_t sts = GetC2ConstGraphicBlock(buffer_pack, &graphic_block); - EXPECT_EQ(sts, C2_OK); - if(nullptr != graphic_block) { - EXPECT_EQ(graphic_block->width(), FRAME_WIDTH); - EXPECT_EQ(graphic_block->height(), FRAME_HEIGHT); - - std::unique_ptr c_graph_view; - sts = MapConstGraphicBlock(*graphic_block, TIMEOUT_NS, &c_graph_view); - EXPECT_EQ(sts, C2_OK); - const uint8_t* const* raw = c_graph_view->data(); - EXPECT_NE(raw, nullptr); - CheckFilledBuffer(raw[0], frame_index); - } - } else { - ADD_FAILURE() << "unexpected value of output_type_"; - } - } - } - { - std::lock_guard lock(expectations_mutex_); - // if collected all expected frames - if(frame_expected_ >= FRAME_COUNT) { - done_.set_value(); - } - } - } - - void onTripped_nb(std::weak_ptr component, - std::vector> settingResult) override - { - (void)component; - (void)settingResult; - EXPECT_EQ(true, false) << "onTripped_nb callback shouldn't come"; - } - - void onError_nb(std::weak_ptr component, - uint32_t errorCode) override - { - (void)component; - (void)errorCode; - EXPECT_EQ(true, false) << "onError_nb callback shouldn't come"; - } - -public: - std::mutex expectations_mutex_; - uint64_t frame_expected_ = 0; // frame index is next to come - C2BufferData::type_t output_type_; - std::promise done_; // fire when all expected frames came -}; - -// Tests how the mock component processes a sequence of C2Work items, in encoder way. -// It accepts c2 frame buffers and allocates output of c2 linear buffer the same size. -// The component copies input buffer to output without any changes. -// The test checks that order of inputs is not changed -// and output is accurately the same as input. -// Also the component processing should make output within 10 seconds (test on hang). -// All supplementary entities (c2 buffers and command queues) are tested by this test. -TEST(MfxMockComponent, Encode) -{ - MfxC2Component::CreateConfig config{}; - c2_status_t sts = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT_ENC, config, reflector, &sts)); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(component, nullptr); - if(nullptr != component) { - - for (uint64_t consumer_memory_type : - { (uint64_t)C2MemoryUsage::CPU_READ, (uint64_t)android::C2AndroidMemoryUsage::HW_CODEC_READ } ) { - - std::shared_ptr validator = - std::make_unique(C2BufferData::LINEAR); - c2_blocking_t may_block {}; - component->setListener_vb(validator, may_block); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - for(uint32_t frame_index = 0; frame_index < FRAME_COUNT; ++frame_index) { - // prepare worklet and push - std::unique_ptr work; - - // insert input data - PrepareWork(frame_index, component, &work, C2BufferData::GRAPHIC, consumer_memory_type); - std::list> works; - works.push_back(std::move(work)); - - sts = component->queue_nb(&works); - EXPECT_EQ(sts, C2_OK); - } - - std::future future = validator->GetFuture(); - std::future_status future_sts = future.wait_for(std::chrono::seconds(10)); - EXPECT_EQ(future_sts, std::future_status::ready); - - component->setListener_vb(nullptr, may_block); - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - validator = nullptr; - } - } -} - -// Tests how the mock component processes a sequence of C2Work items, in decoder way. -// It accepts c2 linear buffer and allocates c2 frame buffer length >= of input. -// The component copies input buffer to output without any changes. -// Leftover of output, if any, is filled with zeroes. -// The test checks that order of inputs is not changed -// and output is accurately the same as input. -// Also the component processing should make output within 10 seconds (test on hang). -// All supplementary entities (c2 buffers and command queues) are tested by this test. -TEST(MfxMockComponent, Decode) -{ - MfxC2Component::CreateConfig config{}; - c2_status_t sts = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT_DEC, config, reflector, &sts)); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(component, nullptr); - if(nullptr != component) { - - for (uint64_t producer_memory_type : - { (uint64_t)C2MemoryUsage::CPU_WRITE, (uint64_t)android::C2AndroidMemoryUsage::HW_CODEC_WRITE } ) { - - std::shared_ptr validator = - std::make_unique(C2BufferData::GRAPHIC); - c2_blocking_t may_block {}; - component->setListener_vb(validator, may_block); - - if(component != nullptr) { - - std::shared_ptr component_intf = component->intf(); - EXPECT_NE(component_intf, nullptr); - - if (!component_intf) continue; - - C2ProducerMemoryType memory_type_setting(producer_memory_type); - sts = component_intf->config_vb( { &memory_type_setting }, may_block, nullptr ); - EXPECT_EQ(sts, C2_OK); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - for(uint32_t frame_index = 0; frame_index < FRAME_COUNT; ++frame_index) { - // prepare worklet and push - std::unique_ptr work; - - // insert input data - PrepareWork(frame_index, component, &work, C2BufferData::LINEAR, C2MemoryUsage::CPU_READ); - std::list> works; - works.push_back(std::move(work)); - - sts = component->queue_nb(&works); - EXPECT_EQ(sts, C2_OK); - } - } - - std::future future = validator->GetFuture(); - std::future_status future_sts = future.wait_for(std::chrono::seconds(10)); - EXPECT_EQ(future_sts, std::future_status::ready); - - component->setListener_vb(nullptr, may_block); - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - validator = nullptr; - } - } -} - -class C2ComponentStateListener : public C2Component::Listener -{ -private: - void onWorkDone_nb(std::weak_ptr, std::list>) {} - - void onTripped_nb(std::weak_ptr, - std::vector>) - { - tripped_notified_.set_value(); - } - - void onError_nb(std::weak_ptr, uint32_t) - { - error_notified_.set_value(); - } - - bool WaitFor(std::promise* promise) - { - bool result = (std::future_status::ready == - promise->get_future().wait_for(std::chrono::seconds(1))); - *promise = std::promise(); // prepare for next notification - return result; - } - -private: - std::promise tripped_notified_; - - std::promise error_notified_; - -public: - bool WaitTrippedState() { return WaitFor(&tripped_notified_); } - - bool WaitErrorState() { return WaitFor(&error_notified_); } -}; - -// Checks the correctness of mock component state machine. -// The component should be able to start from STOPPED (initial) state, -// stop from RUNNING state. Otherwise, C2_BAD_STATE should be returned. -TEST(MfxMockComponent, State) -{ - MfxC2Component::CreateConfig config{}; - c2_status_t sts = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, config, reflector, &sts)); - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(component, nullptr); - if(nullptr != component) { - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - sts = component->start(); - EXPECT_EQ(sts, C2_BAD_STATE); - - sts = component->release(); - EXPECT_EQ(sts, C2_OK); - - component = nullptr; - - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, config, reflector, &sts)); - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(component, nullptr); - - if (nullptr != component) { - std::shared_ptr state_listener = - std::make_shared(); - sts = component->setListener_vb(state_listener, c2_blocking_t{}); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - - sts = component->stop(); - EXPECT_EQ(sts, C2_BAD_STATE); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - auto trip_component = [&] () { - C2TrippedTuning tripped_tuning; - tripped_tuning.value = C2_TRUE; - c2_blocking_t may_block{}; - sts = component->intf()->config_vb({&tripped_tuning}, may_block, nullptr); - EXPECT_EQ(sts, C2_OK); - }; - - trip_component(); - EXPECT_TRUE(state_listener->WaitTrippedState()); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - trip_component(); - EXPECT_TRUE(state_listener->WaitTrippedState()); - - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - auto fail_component = [&] () { - std::list> null_work; - null_work.push_back(nullptr); - - sts = component->queue_nb(&null_work); // cause component error - EXPECT_EQ(sts, C2_OK); - }; - - fail_component(); - EXPECT_TRUE(state_listener->WaitErrorState()); - - sts = component->start(); - EXPECT_EQ(sts, C2_BAD_STATE); - - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - - // Next start, fail, stop series to check for deadlock. - // stop right after enqueuing work leads component to process the work - // during stop operation, so state change (to ERROR) takes place while - // changing to STOPPED. That caused deadlock on MfxC2Component::state_mutex_. - // To prevent this MfxC2Component::next_state_ was introduced. - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - fail_component(); - - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - - sts = component->release(); - EXPECT_EQ(sts, C2_OK); - - sts = component->release(); - EXPECT_EQ(sts, C2_DUPLICATE); - } - } -} - -// Tests mayBlock option handling of config_vb and query_vb methods. -// config_vb should be mutually blocked with state change, -// should return C2_BLOCKING result while state change for C2_DONT_BLOCK option and -// wait for state change completion for C2_MAY_BLOCK option. -// query_vb should always return C2_OK as it is not affected by running->stopped -// state change and not supported in RELEASED state only. -TEST(MfxMockComponent, ConfigQueryBlocking) -{ - MfxC2Component::CreateConfig config{}; - c2_status_t sts = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT_DEC, config, reflector, &sts)); - std::shared_ptr component_intf; - - EXPECT_EQ(sts, C2_OK); - EXPECT_NE(component, nullptr); - if (nullptr != component) { - component_intf = component->intf(); - EXPECT_NE(component_intf, nullptr); - } - - if (component_intf) { - - // Calls config_vb and query_vb methods with specified blocking and checking result to be expected - auto test_config_query = [component_intf] (c2_blocking_t blocking, c2_status_t expected) { - C2ProducerMemoryType some_setting{C2MemoryUsage::CPU_WRITE}; // any setting supported by the component - c2_status_t sts = component_intf->config_vb({&some_setting}, blocking, nullptr); - EXPECT_EQ(sts, expected); - sts = component_intf->query_vb({&some_setting}, {}, blocking, {}); - EXPECT_EQ(sts, C2_OK); - }; - - // This class implements listener interface - // and hangs in onWorkDone callback blocking C2Component from handling works. - // It hangs stop() method too as stop() tries to complete queued works. - // It allows handling on Unblock call. - class Blocker : public C2Component::Listener - { - private: - void onWorkDone_nb(std::weak_ptr, std::list>) override - { - // As lock_ is pre-locked on mutex_ this line blocks execution - // until Unblock method is called. - std::lock_guard lock(mutex_); - } - void onTripped_nb(std::weak_ptr, - std::vector>) override {} - - void onError_nb(std::weak_ptr, uint32_t) override {} - private: - std::mutex mutex_; - std::unique_lock lock_{mutex_}; // it constructs lock_ locked on mutex_ - public: - void Unblock() { - lock_.unlock(); - } - }; - std::shared_ptr blocker = std::make_shared(); - - sts = component->setListener_vb(blocker, C2_DONT_BLOCK/*option for setting this listener*/); - EXPECT_EQ(sts, C2_OK); - - sts = component->start(); - EXPECT_EQ(sts, C2_OK); - - for(uint32_t frame_index = 0; frame_index < FRAME_COUNT; ++frame_index) { - // prepare worklet and push - std::unique_ptr work; - - // insert input data - PrepareWork(frame_index, component, &work, C2BufferData::LINEAR, C2MemoryUsage::CPU_READ); - std::list> works; - works.push_back(std::move(work)); - - sts = component->queue_nb(&works); - EXPECT_EQ(sts, C2_OK); - } - - // Call config_vb/query_vb with C2_DONT_BLOCK while the component is in stable (RUNNING) state, - // no internal state blocking expected, methods should be able to run momentarily, without errors. - test_config_query(C2_DONT_BLOCK, C2_OK); - - // Run stop and simultanouesly test config_vb/query_vb methods. stop should finish enqueued tasks - // what takes some time to switch to STOPPED state. That transition blocks config_vb/query_vb from execution. - // This means that config_vb/query_vb should fail with C2_DONT_BLOCK option and succeed with C2_MAY_BLOCK. - std::thread another_thread([&] () { - - C2ProducerMemoryType some_setting{C2MemoryUsage::CPU_WRITE}; // any setting supported by the component - // If config_vb is OK, state is not transition, stop() not started yet - while (C2_OK == component_intf->config_vb({&some_setting}, C2_DONT_BLOCK, nullptr)) { - std::this_thread::sleep_for(std::chrono::milliseconds(10)); - } - - test_config_query(C2_DONT_BLOCK, C2_BLOCKING); - blocker->Unblock(); // Allow stop() to be continued - test_config_query(C2_MAY_BLOCK, C2_OK); - }); - sts = component->stop(); - EXPECT_EQ(sts, C2_OK); - - another_thread.join(); - } -} diff --git a/unittests/src/c2_utils_test.cpp b/unittests/src/c2_utils_test.cpp deleted file mode 100755 index 9ac55a64..00000000 --- a/unittests/src/c2_utils_test.cpp +++ /dev/null @@ -1,1377 +0,0 @@ -// Copyright (c) 2017-2021 Intel Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -#include -#include "mfx_cmd_queue.h" -#include "mfx_pool.h" -#include "mfx_gralloc_allocator.h" -#include "mfx_va_allocator.h" -#include "mfx_frame_pool_allocator.h" -#include "C2PlatformSupport.h" -#include "mfx_c2_utils.h" -#include -#include -#include "test_streams.h" -#include "streams/h264/stream_nv12_176x144_cqp_g30_100.264.h" -#include "streams/h264/stream_nv12_352x288_cqp_g15_100.264.h" -#include "streams/h265/stream_nv12_176x144_cqp_g30_100.265.h" -#include "streams/h265/stream_nv12_352x288_cqp_g15_100.265.h" -#include "streams/vp9/stream_nv12_176x144_cqp_g30_100.vp9.ivf.h" -#include "streams/vp9/stream_nv12_352x288_cqp_g15_100.vp9.ivf.h" -#include "mfx_c2_component.h" -#include "mfx_c2_components_registry.h" - -#ifdef LIBVA_SUPPORT -#include "mfx_dev_va.h" -#include -#else -#include "mfx_dev_android.h" -#endif - -using namespace android; - -static const size_t CMD_COUNT = 10; - -#define MOCK_COMPONENT_ENC "c2.intel.mock.encoder" -#define MOCK_COMPONENT MOCK_COMPONENT_ENC // use encoder for common tests - -static std::vector g_streams { - &stream_nv12_176x144_cqp_g30_100_264, - &stream_nv12_352x288_cqp_g15_100_264, - &stream_nv12_176x144_cqp_g30_100_265, - &stream_nv12_352x288_cqp_g15_100_265, - &stream_nv12_176x144_cqp_g30_100_vp9_ivf, - &stream_nv12_352x288_cqp_g15_100_vp9_ivf -}; - -// Multiple streams read till the end with CombinedStreamReader should give the same output -// as those streams read with SingleStreamReader instances. -TEST(CombinedStreamReader, Read) -{ - bool header {}; - StreamDescription::Region region; - - std::list> single_readers_res; - { - std::vector readers; - for (const auto& stream : g_streams ) { - readers.emplace_back(stream); - } - - for (auto& reader : readers) { - while (reader.Read(StreamReader::Slicing::Frame(), ®ion, &header)) { - single_readers_res.push_back(reader.GetRegionContents(region)); - } - } - } - - std::list> combined_reader_res; - { - CombinedStreamReader reader(g_streams); - - while (reader.Read(StreamReader::Slicing::Frame(), ®ion, &header)) { - combined_reader_res.push_back(reader.GetRegionContents(region)); - } - } - - EXPECT_EQ(single_readers_res, combined_reader_res); -} - -// Reads from stream by one byte, EndOfStream should give true iff next Read is imposssible. -TEST(CombinedStreamReader, EndOfStream) -{ - CombinedStreamReader reader(g_streams); - - bool header {}; - StreamDescription::Region region; - - for (;;) { - bool eos = reader.EndOfStream(); - bool read_ok = reader.Read(StreamReader::Slicing(1), ®ion, &header); - EXPECT_NE(read_ok, eos); - if (!read_ok) break; - } -} - -// Seek to position around edge between adjacent streams, read some chunk of data -// and compares it with part in all contents array. -TEST(CombinedStreamReader, Seek) -{ - size_t total_size = 0; - for (const auto& stream : g_streams) { - total_size += stream->data.size(); - } - - bool header {}; - StreamDescription::Region region; - - std::vector combined_reader_res; - CombinedStreamReader reader(g_streams); - - while (reader.Read(StreamReader::Slicing(1024), ®ion, &header)) { - std::vector chunk = reader.GetRegionContents(region); - combined_reader_res.insert(combined_reader_res.end(), - chunk.begin(), chunk.end()); - } - EXPECT_EQ(combined_reader_res.size(), total_size); // check read all contents - - const size_t len = 100; // len to read - size_t edge = 0; - for (size_t i = 0; i < g_streams.size() - 1; ++i) { - edge += g_streams[i]->data.size(); - - for (size_t start : { edge - len, edge - len / 2, edge, edge + len, edge - len } ) { - reader.Seek(start); - bool res = reader.Read(StreamReader::Slicing(len), ®ion, &header); - EXPECT_TRUE(res); - std::vector chunk = reader.GetRegionContents(region); - - auto mismatch_res = std::mismatch(chunk.begin(), chunk.end(), - combined_reader_res.begin() + start); - EXPECT_EQ(mismatch_res.first, chunk.end()); - } - } -} - -// Tests abstract command queue processed all supplied tasks in correct order. -TEST(MfxCmdQueue, ProcessAll) -{ - MfxCmdQueue queue; - queue.Start(); - - std::vector result; - - for(size_t i = 0; i < CMD_COUNT; ++i) { - - std::unique_ptr ptr_i = std::make_unique(i); - - // lambda mutable and not copy-assignable to assert MfxCmdQueue supports it - auto task = [ ptr_i = std::move(ptr_i), &result] () mutable { - result.push_back(*ptr_i); - ptr_i = nullptr; - }; - - queue.Push(std::move(task)); - } - - queue.Stop(); - - EXPECT_EQ(result.size(), CMD_COUNT); - for(size_t i = 0; i < result.size(); ++i) { - EXPECT_EQ(result[i], i); - } -} - -// Tests command queue doesn't crash on invalid Start/Stop sequences. -TEST(MfxCmdQueue, StartStop) -{ - { // double start - MfxCmdQueue queue; - queue.Start(); - queue.Start(); - } - { // double stop - MfxCmdQueue queue; - queue.Start(); - queue.Stop(); - queue.Stop(); - } - { // stop not started - MfxCmdQueue queue; - queue.Stop(); - } - { // destruct running queue - MfxCmdQueue queue; - queue.Start(); - } -} - -// Tests abstract command queue handles Pause/Resume fine. -TEST(MfxCmdQueue, PauseResume) -{ - MfxCmdQueue queue; - queue.Start(); - - std::vector result; - const size_t PAUSE_CMD_INDEX = CMD_COUNT / 2; - - for(size_t i = 0; i < CMD_COUNT; ++i) { - - std::unique_ptr ptr_i = std::make_unique(i); - - // lambda mutable and not copy-assignable to assert MfxCmdQueue supports it - auto task = [ ptr_i = std::move(ptr_i), &queue, &result] () mutable { - result.push_back(*ptr_i); - if (*ptr_i == PAUSE_CMD_INDEX) { - queue.Pause(); - } - ptr_i = nullptr; - }; - - queue.Push(std::move(task)); - } - // Wait long enough to run into task with queue.Pause. - std::this_thread::sleep_for(std::chrono::milliseconds(200)); - // Expect tasks processed till PAUSE_CMD_INDEX including. - EXPECT_EQ(result.size(), PAUSE_CMD_INDEX + 1); - for(size_t i = 0; i < result.size(); ++i) { - EXPECT_EQ(result[i], i); - } - - queue.Resume(); - queue.Stop(); - // Resume and Stop above should complete tasks remained. - EXPECT_EQ(result.size(), CMD_COUNT); - for(size_t i = PAUSE_CMD_INDEX + 1; i < result.size(); ++i) { - EXPECT_EQ(result[i], i); - } -} - -// Tests that MfxCmdQueue::Stop is waiting for the end of all pushed tasks. -TEST(MfxCmdQueue, Stop) -{ - MfxCmdQueue queue; - queue.Start(); - - std::vector result; - - auto timeout = std::chrono::milliseconds(1); - for(size_t i = 0; i < CMD_COUNT; ++i) { - - queue.Push( [i, timeout, &result] { - - std::this_thread::sleep_for(timeout); - result.push_back(i); - } ); - - // progressively increase timeout to be sure some of them will not be processed - // by moment of Stop - timeout *= 2; - } - - queue.Stop(); - - EXPECT_EQ(result.size(), CMD_COUNT); // all commands should be executed - for(size_t i = 0; i < result.size(); ++i) { - EXPECT_EQ(result[i], i); - } -} - -// Tests that MfxCmdQueue::Abort is not waiting for the end of all pushed tasks. -// At least some tasks should not be processed. -TEST(MfxCmdQueue, Abort) -{ - MfxCmdQueue queue; - queue.Start(); - - std::vector result; - - auto timeout = std::chrono::milliseconds(1); - for(size_t i = 0; i < CMD_COUNT; ++i) { - - queue.Push( [i, timeout, &result] { - - std::this_thread::sleep_for(timeout); - result.push_back(i); - } ); - - // progressively increase timeout to be sure some of them will not be processed - timeout *= 2; - } - - queue.Abort(); - - EXPECT_EQ(result.size() < CMD_COUNT, true); // some commands must be dropped - for(size_t i = 0; i < result.size(); ++i) { - EXPECT_EQ(result[i], i); - } -} - -// Tests that MfxPool allocates values among appended -// and if no resources available, correctly waits for freeing resources. -// Also checks allocated values are valid after pool destruction. -TEST(MfxPool, Alloc) -{ - const int COUNT = 10; - std::shared_ptr allocated_again[COUNT]; - - { - MfxPool pool; - // append range of numbers - for (int i = 0; i < COUNT; ++i) { - pool.Append(std::make_unique(i)); - } - - std::shared_ptr allocated[COUNT]; - for (int i = 0; i < COUNT; ++i) { - allocated[i] = pool.Alloc(); - EXPECT_EQ(*allocated[i], i); // check values are those appended - } - - std::thread free_thread([&] { - std::this_thread::sleep_for(std::chrono::milliseconds(500)); - for (int i = 0; i < COUNT; ++i) { - allocated[i].reset(); - } - }); - - auto start = std::chrono::system_clock::now(); - for (int i = 0; i < COUNT; ++i) { - allocated_again[i] = pool.Alloc(); // this Alloc should wait for free in free_thread - EXPECT_EQ(*allocated_again[i], i); // and got the same value - } - auto end = std::chrono::system_clock::now(); - std::chrono::duration elapsed_seconds = end - start; - // elapsed time is around 0.5 seconds when free_thread deallocates the resources - EXPECT_TRUE((0.4 < elapsed_seconds.count()) && (elapsed_seconds.count() < 0.6)); - - free_thread.join(); - - start = std::chrono::system_clock::now(); - std::shared_ptr allocation_failed = pool.Alloc(); - EXPECT_EQ(allocation_failed, nullptr); // should get null in timeout value (1 second) - end = std::chrono::system_clock::now(); - elapsed_seconds = end - start; - EXPECT_TRUE((0.9 < elapsed_seconds.count()) && (elapsed_seconds.count() < 1.1)); - } - // check allocated_again have correct values after pool destruction - for (int i = 0; i < COUNT; ++i) { - EXPECT_EQ(*allocated_again[i], i); - } -} - -// Tests MfxDev could be created and released significant amount of times. -// For pure build this tests MfxDevAndroid, for VA - MfxDevVa. -TEST(MfxDev, InitCloseNoLeaks) -{ - const int COUNT = 1500; - - for (int i = 0; i < COUNT; ++i) - { - std::unique_ptr device; - mfxStatus sts = MfxDev::Create(MfxDev::Usage::Decoder, &device); - - EXPECT_EQ(MFX_ERR_NONE, sts); - EXPECT_NE(device, nullptr); - - sts = device->Close(); - EXPECT_EQ(MFX_ERR_NONE, sts); - } -} - -static void CheckNV12PlaneLayout(uint16_t width, uint16_t height, const C2PlanarLayout& layout, - const uint8_t* const* data) -{ - using Layout = C2PlanarLayout; - using Info = C2PlaneInfo; - - EXPECT_EQ(layout.type, Layout::TYPE_YUV); - EXPECT_EQ(layout.numPlanes, 3u); - EXPECT_EQ(layout.rootPlanes, 2u); - - std::map expected_channels = { - { Layout::PLANE_Y, Info::CHANNEL_Y }, - { Layout::PLANE_U, Info::CHANNEL_CB }, - { Layout::PLANE_V, Info::CHANNEL_CR }, - }; - - for (Layout::plane_index_t index : { Layout::PLANE_Y, Layout::PLANE_U, Layout::PLANE_V }) { - EXPECT_EQ(layout.planes[index].channel, expected_channels[index]); - EXPECT_EQ(layout.planes[index].colInc, index == Layout::PLANE_Y ? 1 : 2); - EXPECT_TRUE(layout.planes[index].rowInc >= width); - EXPECT_EQ(layout.planes[index].colSampling, index == Layout::PLANE_Y ? 1u : 2u); - EXPECT_EQ(layout.planes[index].rowSampling, index == Layout::PLANE_Y ? 1u : 2u); - EXPECT_EQ(layout.planes[index].bitDepth, 8u); - EXPECT_EQ(layout.planes[index].allocatedDepth, 8u); - EXPECT_EQ(layout.planes[index].rightShift, 0u); - EXPECT_EQ(layout.planes[index].endianness, C2PlaneInfo::NATIVE); - EXPECT_EQ(layout.planes[index].rootIx, index == Layout::PLANE_Y ? Layout::PLANE_Y : Layout::PLANE_U); - EXPECT_EQ(layout.planes[index].offset, index != Layout::PLANE_V ? 0u : 1u); - - EXPECT_NE(data[index], nullptr); - if (index != Layout::PLANE_Y) { - EXPECT_TRUE(data[index] - data[0] >= layout.planes[Layout::PLANE_Y].rowInc * height); - } - } - EXPECT_EQ(data[Layout::PLANE_U] + 1, data[Layout::PLANE_V]); -} - -#ifdef LIBVA_SUPPORT - -static void CheckMfxFrameData(mfxU32 fourcc, uint16_t width, uint16_t height, - bool hw_memory, bool locked, const mfxFrameData& frame_data) -{ - EXPECT_EQ(frame_data.PitchHigh, 0); - uint32_t pitch = MakeUint32(frame_data.PitchHigh, frame_data.PitchLow); - - if (fourcc != MFX_FOURCC_P8) { - EXPECT_TRUE(pitch >= width); - } - EXPECT_EQ(frame_data.MemId != nullptr, hw_memory); - - bool pointers_expected = locked || !hw_memory; - bool color = (fourcc != MFX_FOURCC_P8); - - EXPECT_EQ(pointers_expected, frame_data.Y != nullptr); - EXPECT_EQ(pointers_expected && color, frame_data.UV != nullptr); - EXPECT_EQ(pointers_expected && color, frame_data.V != nullptr); - - if(pointers_expected && color) { - EXPECT_TRUE(frame_data.Y + pitch * height <= frame_data.UV); - EXPECT_EQ(frame_data.UV + 1, frame_data.V); - } - EXPECT_EQ(frame_data.A, nullptr); -} - -#endif - -static uint8_t PlanePixelValue(uint16_t x, uint16_t y, uint32_t plane_index, int frame_index) -{ - return (uint8_t)(x + y + plane_index + frame_index); -} - -typedef std::function ProcessPlanePixel; - -static void ForEveryPlanePixel(uint16_t width, uint16_t height, const C2PlanarLayout& layout, - const ProcessPlanePixel& process_function, uint8_t* const* data) -{ - for (uint32_t i = 0; i < layout.numPlanes; ++i) { - const C2PlaneInfo& plane = layout.planes[i]; - - uint8_t* row = data[i]; - for (uint16_t y = 0; y < height; y += plane.rowSampling) { - uint8_t* pixel = row; - for (uint16_t x = 0; x < width; x += plane.colSampling) { - process_function(x, y, i, pixel); - pixel += plane.colInc; - } - row += plane.rowInc; - } - } -} - -#ifdef LIBVA_SUPPORT - -static void ForEveryPlanePixel(uint16_t width, uint16_t height, const mfxFrameInfo& frame_info, - const ProcessPlanePixel& process_function, const mfxFrameData& frame_data) -{ - const int planes_count_max = 3; - uint8_t* planes_data[planes_count_max] = { frame_data.Y, frame_data.UV, frame_data.UV + 1 }; - const uint16_t planes_vert_subsampling[planes_count_max] = { 1, 2, 2 }; - const uint16_t planes_horz_subsampling[planes_count_max] = { 1, 2, 2 }; - const uint16_t planes_col_inc[planes_count_max] = { 1, 2, 2 }; - - int planes_count = -1; - - switch (frame_info.FourCC) { - case MFX_FOURCC_NV12: - EXPECT_EQ(frame_info.ChromaFormat, MFX_CHROMAFORMAT_YUV420); - planes_count = 3; - break; - case MFX_FOURCC_P8: - EXPECT_EQ(frame_info.ChromaFormat, MFX_CHROMAFORMAT_MONOCHROME); - planes_count = 1; - // buffer is linear, set up width and height to one line - width = EstimatedEncodedFrameLen(width, height); - height = 1; - break; - default: - EXPECT_TRUE(false) << "unsupported color format"; - } - - uint32_t pitch = MakeUint32(frame_data.PitchHigh, frame_data.PitchLow); - - for (int i = 0; i < planes_count; ++i) { - - uint8_t* row = planes_data[i]; - for (uint16_t y = 0; y < height; y += planes_vert_subsampling[i]) { - uint8_t* pixel = row; - for (uint16_t x = 0; x < width; x += planes_horz_subsampling[i]) { - process_function(x, y, i, pixel); - pixel += planes_col_inc[i]; - } - row += pitch; - } - } -} - -#endif - -// Fills frame planes with PlanePixelValue pattern. -// Value should depend on plane index, frame index, x and y. -template -static void FillFrameContents(uint16_t width, uint16_t height, int frame_index, - const FrameInfo& frame_info, const FrameData& frame_data) -{ - ProcessPlanePixel process = [frame_index] (uint16_t x, uint16_t y, uint32_t plane_index, uint8_t* plane_pixel) { - *plane_pixel = PlanePixelValue(x, y, plane_index, frame_index); - }; - ForEveryPlanePixel(width, height, frame_info, process, frame_data); -} - -template -static void CheckFrameContents(uint16_t width, uint16_t height, int frame_index, - const FrameInfo& frame_info, const FrameData& frame_data) -{ - int fails_count = 0; - - ProcessPlanePixel process = [frame_index, &fails_count] (uint16_t x, uint16_t y, uint32_t plane_index, uint8_t* plane_pixel) { - if (fails_count < 10) { // to not overflood output - uint8_t actual = *plane_pixel; - uint8_t expected = PlanePixelValue(x, y, plane_index, frame_index); - bool match = (actual == expected); - if (!match) ++fails_count; - EXPECT_TRUE(match) << NAMED(x) << NAMED(y) << NAMED(plane_index) - << NAMED((int)actual) << NAMED((int)expected); - } - }; - ForEveryPlanePixel(width, height, frame_info, process, frame_data); -} - -// Tests gralloc allocator ability to alloc and free buffers. -// The allocated buffer is locked, filled memory with some pattern, -// unlocked, then locked again, memory pattern should the same. -TEST(MfxGrallocAllocator, BufferKeepsContents) -{ - std::unique_ptr allocator; - - c2_status_t res = MfxGrallocAllocator::Create(&allocator); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(allocator, nullptr); - - const int WIDTH = 600; - const int HEIGHT = 400; - const size_t FRAME_COUNT = 3; - - if (allocator) { - - buffer_handle_t handle[FRAME_COUNT] {}; - c2_status_t res; - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - res = allocator->Alloc(WIDTH, HEIGHT, &handle[i]); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(handle, nullptr); - } - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - uint8_t* data[C2PlanarLayout::MAX_NUM_PLANES] {}; - C2PlanarLayout layout {}; - res = allocator->LockFrame(handle[i], data, &layout); - EXPECT_EQ(res, C2_OK); - - CheckNV12PlaneLayout(WIDTH, HEIGHT, layout, data); - - FillFrameContents(WIDTH, HEIGHT, i, layout, data); - - res = allocator->UnlockFrame(handle[i]); - EXPECT_EQ(res, C2_OK); - } - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - uint8_t* data[C2PlanarLayout::MAX_NUM_PLANES] {}; - C2PlanarLayout layout {}; - res = allocator->LockFrame(handle[i], data, &layout); - EXPECT_EQ(res, C2_OK); - - CheckNV12PlaneLayout(WIDTH, HEIGHT, layout, data); - - CheckFrameContents(WIDTH, HEIGHT, i, layout, data); - - res = allocator->UnlockFrame(handle[i]); - EXPECT_EQ(res, C2_OK); - } - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - res = allocator->Free(handle[i]); - EXPECT_EQ(res, C2_OK); - } - } -} - -#ifdef LIBVA_SUPPORT - -static void InitFrameInfo(mfxU32 fourcc, uint16_t width, uint16_t height, mfxFrameInfo* frame_info) -{ - *frame_info = mfxFrameInfo {}; - frame_info->BitDepthLuma = 8; - frame_info->BitDepthChroma = 8; - frame_info->FourCC = fourcc; - - switch (fourcc) { - case MFX_FOURCC_NV12: - frame_info->ChromaFormat = MFX_CHROMAFORMAT_YUV420; - break; - case MFX_FOURCC_P8: - frame_info->ChromaFormat = MFX_CHROMAFORMAT_MONOCHROME; - break; - default: - ASSERT_TRUE(false) << std::hex << fourcc << " format is not supported"; - } - - frame_info->Width = width; - frame_info->Height = height; - frame_info->CropX = 0; - frame_info->CropY = 0; - frame_info->CropW = width; - frame_info->CropH = height; - frame_info->FrameRateExtN = 30; - frame_info->FrameRateExtD = 1; - frame_info->PicStruct = MFX_PICSTRUCT_PROGRESSIVE; -} - -class UtilsVaContext -{ -private: - VAConfigID va_config_ { VA_INVALID_ID }; - - VAContextID va_context_ { VA_INVALID_ID }; - - VADisplay va_display_ { nullptr }; - -public: - UtilsVaContext(VADisplay va_display, int width, int height) - : va_display_(va_display) - { - VAConfigAttrib attrib[2]; - mfxI32 numAttrib = MFX_GET_ARRAY_SIZE(attrib); - attrib[0].type = VAConfigAttribRTFormat; - attrib[0].value = VA_RT_FORMAT_YUV420; - attrib[1].type = VAConfigAttribRateControl; - attrib[1].value = VA_RC_CQP; - - mfxU32 flag = VA_PROGRESSIVE; - - VAProfile va_profile = VAProfileH264ConstrainedBaseline; - VAEntrypoint entrypoint = VAEntrypointEncSlice; - VAStatus sts = vaCreateConfig(va_display_, va_profile, entrypoint, attrib, numAttrib, &va_config_); - EXPECT_EQ(sts, VA_STATUS_SUCCESS); - EXPECT_NE(va_config_, VA_INVALID_ID); - - if (VA_INVALID_ID != va_config_) { - sts = vaCreateContext(va_display_, va_config_, width, height, flag, nullptr, 0, &va_context_); - EXPECT_EQ(sts, VA_STATUS_SUCCESS); - EXPECT_NE(va_context_, VA_INVALID_ID); - } - } - - ~UtilsVaContext() - { - if (va_config_ != VA_INVALID_ID) vaDestroyConfig(va_display_, va_config_); - if (va_context_ != VA_INVALID_ID) vaDestroyContext(va_display_, va_context_); - } - - VAContextID GetVaContext() { return va_context_; } -}; - -struct MfxAllocTestRun { - int width; - int height; - int frame_count; - mfxU32 fourcc; -}; - -typedef std::function MfxVaAllocatorTestStep; - -static void MfxVaAllocatorTest(const std::vector& steps, int repeat_count = 1) -{ - MfxDevVa* dev_va = new MfxDevVa(MfxDev::Usage::Encoder); - std::unique_ptr dev { dev_va }; - - mfxStatus sts = dev->Init(); - EXPECT_EQ(MFX_ERR_NONE, sts); - - std::shared_ptr allocator = dev->GetFrameAllocator(); - EXPECT_NE(allocator, nullptr); - - if (allocator) { - - MfxAllocTestRun test_allocations[] { - { 600, 400, 3, MFX_FOURCC_NV12 }, - { 320, 240, 2, MFX_FOURCC_NV12 }, - { 1920, 1080, 3, MFX_FOURCC_NV12 }, - { 1280, 720, 3, MFX_FOURCC_P8 }, - }; - - mfxFrameAllocResponse responses[MFX_GET_ARRAY_SIZE(test_allocations)] {}; - mfxFrameAllocRequest requests[MFX_GET_ARRAY_SIZE(test_allocations)] {}; - std::unique_ptr va_contexts[MFX_GET_ARRAY_SIZE(test_allocations)]; - - for (MfxAllocTestRun& run : test_allocations) { - if (run.fourcc == MFX_FOURCC_P8) { - va_contexts[&run - test_allocations] = - std::make_unique(dev_va->GetVaDisplay(), run.width, run.height); - } - } - - for (int i = 0; i < repeat_count; ++i) { - for (auto& step : steps) { - for (const MfxAllocTestRun& run : test_allocations) { - const int index = &run - test_allocations; - mfxFrameAllocResponse& response = responses[index]; - mfxFrameAllocRequest& request = requests[index]; - - if (va_contexts[index]) { - - if (va_contexts[index]->GetVaContext() == VA_INVALID_ID) continue; - - request.AllocId = va_contexts[index]->GetVaContext(); - } - - step(run, allocator.get(), request, response); - } - } - } - - allocator.reset(); - } - sts = dev->Close(); - EXPECT_EQ(MFX_ERR_NONE, sts); -} - -static void MfxFrameAlloc(const MfxAllocTestRun& run, MfxFrameAllocator* allocator, - mfxFrameAllocRequest& request, mfxFrameAllocResponse& response) -{ - request.Type = MFX_MEMTYPE_FROM_ENCODE; - request.NumFrameMin = run.frame_count; - request.NumFrameSuggested = run.frame_count; - InitFrameInfo(run.fourcc, run.width, run.height, &request.Info); - - mfxStatus sts = allocator->AllocFrames(&request, &response); - EXPECT_EQ(sts, MFX_ERR_NONE); - EXPECT_EQ(response.NumFrameActual, request.NumFrameMin); - - EXPECT_NE(response.mids, nullptr); -} - -static void MfxFrameFree(const MfxAllocTestRun&, MfxFrameAllocator* allocator, - mfxFrameAllocRequest&, mfxFrameAllocResponse& response) -{ - mfxStatus sts = allocator->FreeFrames(&response); - EXPECT_EQ(MFX_ERR_NONE, sts); -} - -// Tests mfxFrameAllocator implementation on VA. -// Checks Alloc and Free don't return any errors. -// Repeated many times to check possible memory leaks. -TEST(MfxVaAllocator, AllocFreeNoLeaks) -{ - const int COUNT = 1000; - MfxVaAllocatorTest( { MfxFrameAlloc, MfxFrameFree }, COUNT ); -} - -// Tests mfxFrameAllocator implementation on VA. -// Executes GetFrameHDL on every allocated mem_id and assures all returned handles are different. -TEST(MfxVaAllocator, GetFrameHDL) -{ - std::set all_frame_handles; - auto get_frame_hdl_test = [&all_frame_handles] (const MfxAllocTestRun& run, MfxFrameAllocator* allocator, - mfxFrameAllocRequest&, mfxFrameAllocResponse& response) { - - EXPECT_NE(response.mids, nullptr); - if (response.mids) { - for (int i = 0; i < run.frame_count; ++i) { - EXPECT_NE(response.mids[i], nullptr); - - mfxHDL frame_handle {}; - mfxStatus sts = allocator->GetFrameHDL(response.mids[i], &frame_handle); - EXPECT_EQ(MFX_ERR_NONE, sts); - EXPECT_NE(frame_handle, nullptr); - - EXPECT_EQ(all_frame_handles.find(frame_handle), all_frame_handles.end()); // test uniqueness - all_frame_handles.insert(frame_handle); - } - } - }; - - MfxVaAllocatorTest( { MfxFrameAlloc, get_frame_hdl_test, MfxFrameFree } ); -} - -// Tests mfxFrameAllocator implementation on VA. -// The allocated buffer is locked, memory filled with some pattern, -// unlocked, then locked again, memory pattern should the same. -TEST(MfxVaAllocator, BufferKeepsContents) -{ - const bool hw_memory = true; - const bool locked = true; - - auto lock_frame = [] (const MfxAllocTestRun& run, MfxFrameAllocator* allocator, - mfxFrameAllocRequest& request, mfxFrameAllocResponse& response) { - - for (int i = 0; i < run.frame_count; ++i) { - mfxFrameData frame_data {}; - mfxStatus sts = allocator->LockFrame(response.mids[i], &frame_data); - EXPECT_EQ(MFX_ERR_NONE, sts); - - CheckMfxFrameData(run.fourcc, run.width, run.height, hw_memory, locked, frame_data); - - FillFrameContents(run.width, run.height, i, request.Info, frame_data); - - sts = allocator->UnlockFrame(response.mids[i], &frame_data); - EXPECT_EQ(MFX_ERR_NONE, sts); - } - }; - - auto unlock_frame = [] (const MfxAllocTestRun& run, MfxFrameAllocator* allocator, - mfxFrameAllocRequest& request, mfxFrameAllocResponse& response) { - - for (int i = 0; i < run.frame_count; ++i) { - mfxFrameData frame_data {}; - mfxStatus sts = allocator->LockFrame(response.mids[i], &frame_data); - EXPECT_EQ(MFX_ERR_NONE, sts); - - CheckMfxFrameData(run.fourcc, run.width, run.height, hw_memory, locked, frame_data); - - CheckFrameContents(run.width, run.height, i, request.Info, frame_data); - - sts = allocator->UnlockFrame(response.mids[i], &frame_data); - EXPECT_EQ(MFX_ERR_NONE, sts); - } - }; - - MfxVaAllocatorTest( { MfxFrameAlloc, lock_frame, unlock_frame, MfxFrameFree } ); -} - -typedef std::function MfxFrameConverterTestStep; - -static void MfxFrameConverterTest(const std::vector& steps, int repeat_count = 1) -{ - std::unique_ptr gr_allocator; - - c2_status_t res = MfxGrallocAllocator::Create(&gr_allocator); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(gr_allocator, nullptr); - - std::unique_ptr dev { new MfxDevVa(MfxDev::Usage::Encoder) }; - - mfxStatus sts = dev->Init(); - EXPECT_EQ(MFX_ERR_NONE, sts); - - std::shared_ptr allocator = dev->GetFrameAllocator(); - EXPECT_NE(allocator, nullptr); - - std::shared_ptr converter = dev->GetFrameConverter(); - EXPECT_NE(converter, nullptr); - - if (gr_allocator && allocator && converter) { - for (int i = 0; i < repeat_count; ++i) { - for (auto& step : steps) { - step(gr_allocator.get(), allocator.get(), converter.get()); - } - } - } - - converter->FreeAllMappings(); - converter.reset(); - allocator.reset(); - - sts = dev->Close(); - EXPECT_EQ(MFX_ERR_NONE, sts); -} - -// Class implementing variety of steps for MfxFrameConverter tests. -class MfxFrameConverterTestSteps -{ -public: - using Step = MfxFrameConverterTestStep; - // operation on frame mapped from gralloc to system memory - typedef std::function GrMemOperation; - // operation on frame mapped from va to system memory - typedef std::function VaMemOperation; - -public: - // gralloc allocation step - Step gr_alloc = [this] (MfxGrallocAllocator* gr_allocator, MfxFrameAllocator*, MfxFrameConverter*) { - for (size_t i = 0; i < FRAME_COUNT; ++i) { - c2_status_t res = gr_allocator->Alloc(WIDTH, HEIGHT, &handles[i]); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(handles[i], nullptr); - } - }; - - // c2 graphic allocation step - Step c2_alloc = [this] (MfxGrallocAllocator*, MfxFrameAllocator*, MfxFrameConverter*) { - - std::shared_ptr c2_allocator; - c2_status_t res = C2_OK; - - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, {}, reflector, &res)); - EXPECT_EQ(res, C2_OK); - - res = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &c2_allocator); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(c2_allocator, nullptr); - - if (c2_allocator) { - for (size_t i = 0; i < FRAME_COUNT; ++i) { - res = c2_allocator->fetchGraphicBlock( - WIDTH, HEIGHT, - HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL, - // HW_CODEC forces VNDK mock to allocate gralloc memory, not system - { android::C2AndroidMemoryUsage::HW_CODEC_READ, android::C2AndroidMemoryUsage::HW_CODEC_WRITE }, - &gr_blocks[i]); - - handles[i] = gr_blocks[i]->handle(); - - EXPECT_EQ(res, C2_OK); - EXPECT_NE(handles[i], nullptr); - } - } - }; - - // gralloc deallocation step - Step gr_free = [this] (MfxGrallocAllocator* gr_allocator, MfxFrameAllocator*, MfxFrameConverter*) { - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - c2_status_t res = gr_allocator->Free(handles[i]); - EXPECT_EQ(res, C2_OK); - } - }; - - // c2 deallocation step - Step c2_free = [this] (MfxGrallocAllocator*, MfxFrameAllocator*, MfxFrameConverter*) { - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - gr_blocks[i] = nullptr; - } - }; - - // lambda constructing test step doing: gralloc Lock, some specific work on locked memory, gralloc unlock - Step do_gr_mem_operation(GrMemOperation gr_mem_operation) { - return [&] (MfxGrallocAllocator* gr_allocator, MfxFrameAllocator*, MfxFrameConverter*) { - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - uint8_t* data[C2PlanarLayout::MAX_NUM_PLANES] {}; - C2PlanarLayout layout {}; - c2_status_t res = gr_allocator->LockFrame(handles[i], data, &layout); - EXPECT_EQ(res, C2_OK); - if (C2_OK == res) { - CheckNV12PlaneLayout(WIDTH, HEIGHT, layout, data); - - gr_mem_operation(i, layout, data); - - res = gr_allocator->UnlockFrame(handles[i]); - EXPECT_EQ(res, C2_OK); - } - }; - }; - }; - - // lambda constructing test step doing: c2 Lock, some specific work on locked memory, c2 unlock - Step do_c2_mem_operation(GrMemOperation gr_mem_operation) { - return [&] (MfxGrallocAllocator*, MfxFrameAllocator*, MfxFrameConverter*) { - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - C2Acquirable acquirable = gr_blocks[i]->map(); - C2GraphicView view = acquirable.get(); - EXPECT_EQ(view.error(), C2_OK); - if (C2_OK == view.error()) { - CheckNV12PlaneLayout(WIDTH, HEIGHT, view.layout(), view.data()); - - gr_mem_operation(i, view.layout(), view.data()); - } - }; - }; - }; - - // gralloc to va wiring step - Step gr_convert_to_va = [this] (MfxGrallocAllocator*, MfxFrameAllocator*, MfxFrameConverter* converter) { - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - bool decode_target { false }; - mfxStatus mfx_sts = converter->ConvertGrallocToVa(handles[i], decode_target, &mfx_mem_ids[i]); - EXPECT_EQ(MFX_ERR_NONE, mfx_sts); - EXPECT_NE(mfx_mem_ids[i], nullptr); - } - }; - - // lambda constructing test step doing: va Lock, some specific work on locked memory, va unlock - Step do_va_mem_operation(VaMemOperation va_mem_operation) { - return [&] (MfxGrallocAllocator*, MfxFrameAllocator* allocator, MfxFrameConverter*) { - - const bool hw_memory = true; - const bool locked = true; - - mfxFrameInfo frame_info {}; - InitFrameInfo(MFX_FOURCC_NV12, WIDTH, HEIGHT, &frame_info); - - for (size_t i = 0; i < FRAME_COUNT; ++i) { - mfxFrameData frame_data {}; - mfxStatus sts = allocator->LockFrame(mfx_mem_ids[i], &frame_data); - EXPECT_EQ(MFX_ERR_NONE, sts); - if (MFX_ERR_NONE == sts) { - CheckMfxFrameData(MFX_FOURCC_NV12, WIDTH, HEIGHT, hw_memory, locked, frame_data); - - va_mem_operation(i, frame_info, frame_data); - - sts = allocator->UnlockFrame(mfx_mem_ids[i], &frame_data); - EXPECT_EQ(MFX_ERR_NONE, sts); - } - } - }; - }; - - GrMemOperation fill_gr_frame = [this] (int frame_index, const C2PlanarLayout& layout, uint8_t* const* data) { - FillFrameContents(WIDTH, HEIGHT, frame_index, layout, data); // fill gralloc with pattern #1 - }; - - VaMemOperation check_va_frame = [this] (int frame_index, const mfxFrameInfo& frame_info, mfxFrameData& frame_data) { - CheckFrameContents(WIDTH, HEIGHT, frame_index, frame_info, frame_data); // check pattern #1 in va - }; - - VaMemOperation fill_va_frame = [this] (int frame_index, const mfxFrameInfo& frame_info, mfxFrameData& frame_data) { - // fill va with pattern #2 - FillFrameContents(WIDTH, HEIGHT, FRAME_COUNT - frame_index, frame_info, frame_data); - }; - - GrMemOperation check_gr_frame = [this] (int frame_index, const C2PlanarLayout& layout, uint8_t* const* data) { - // check pattern #2 in gralloc - CheckFrameContents(WIDTH, HEIGHT, FRAME_COUNT - frame_index, layout, data); - }; - - Step free_mappings = [] (MfxGrallocAllocator*, MfxFrameAllocator*, MfxFrameConverter* converter) { - converter->FreeAllMappings(); - }; - -private: - const int WIDTH = 600; - const int HEIGHT = 400; - static constexpr int FRAME_COUNT = 3; - -private: - std::shared_ptr gr_blocks[FRAME_COUNT]; - buffer_handle_t handles[FRAME_COUNT] {}; - mfxMemId mfx_mem_ids[FRAME_COUNT] {}; -}; - -// Allocates some gralloc frames with MfxGrallocAllocator, -// fills them with pattern, -// wires them up with mfxMemID (VA surface inside), -// locks mfxFrames and checks a pattern is the same. -// Then locks mfxFrames again, fills them with different pattern -// and checks original gralloc buffers get updated pattern. -// These steps prove modifications go from gralloc to VA and back. -TEST(MfxFrameConverter, GrallocContentsMappedToVa) -{ - MfxFrameConverterTestSteps steps; - // test direct gralloc allocation wiring to va - MfxFrameConverterTest( { - steps.gr_alloc, - steps.do_gr_mem_operation(steps.fill_gr_frame), - steps.gr_convert_to_va, - steps.do_va_mem_operation(steps.check_va_frame), - steps.do_va_mem_operation(steps.fill_va_frame), - steps.do_gr_mem_operation(steps.check_gr_frame), - steps.free_mappings, - steps.gr_free - } ); -} - -// Allocates some C2GraphicBlock instances with C2 vndk GrallocAllocator, -// fills them with pattern, -// wires them up with mfxMemID (VA surface inside), -// locks mfxFrames and checks a pattern is the same. -// Then locks mfxFrames again, fills them with different pattern -// and checks original gralloc buffers get updated pattern. -// These steps prove modifications go from C2GraphicBlock to VA and back. -TEST(MfxFrameConverter, C2GraphicBlockContentsMappedToVa) -{ - MfxFrameConverterTestSteps steps; - // test c2 vndk allocation wiring to va - MfxFrameConverterTest( { - steps.c2_alloc, - steps.do_c2_mem_operation(steps.fill_gr_frame), - steps.gr_convert_to_va, - steps.do_va_mem_operation(steps.check_va_frame), - steps.do_va_mem_operation(steps.fill_va_frame), - steps.do_c2_mem_operation(steps.check_gr_frame), - steps.free_mappings, - steps.c2_free - } ); -} - -// Allocates and maps gralloc handles to VA. -// Then frees resources in different ways, checks it works -// significant amount of times. -TEST(MfxFrameConverter, NoLeaks) -{ - const int WIDTH = 1920; - const int HEIGHT = 1080; - const int REPEAT_COUNT = 500; - - buffer_handle_t handle {}; - mfxMemId mfx_mem_id {}; - - auto alloc_and_map = [&] (MfxGrallocAllocator* gr_allocator, MfxFrameAllocator*, - MfxFrameConverter* converter) { - - c2_status_t res = gr_allocator->Alloc(WIDTH, HEIGHT, &handle); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(handle, nullptr); - - bool decode_target { false }; - mfxStatus mfx_sts = converter->ConvertGrallocToVa(handle, decode_target, &mfx_mem_id); - EXPECT_EQ(MFX_ERR_NONE, mfx_sts); - EXPECT_NE(mfx_mem_id, nullptr); - }; - - auto gr_free = [&] (MfxGrallocAllocator* gr_allocator, MfxFrameAllocator*, MfxFrameConverter*) { - c2_status_t res = gr_allocator->Free(handle); - EXPECT_EQ(res, C2_OK); - }; - - auto free_all = [] (MfxGrallocAllocator*, MfxFrameAllocator*, MfxFrameConverter* converter) { - converter->FreeAllMappings(); - }; - - MfxFrameConverterTest( { alloc_and_map, free_all, gr_free }, REPEAT_COUNT ); - - auto free_by_handles = [&] (MfxGrallocAllocator*, MfxFrameAllocator*, - MfxFrameConverter* converter) { - - //converter->FreeGrallocToVaMapping(handle); - }; - - MfxFrameConverterTest( { alloc_and_map, free_by_handles, gr_free }, REPEAT_COUNT ); - - auto free_by_mids = [&] (MfxGrallocAllocator*, MfxFrameAllocator*, - MfxFrameConverter* converter) { - - converter->FreeGrallocToVaMapping(mfx_mem_id); - }; - - MfxFrameConverterTest( { alloc_and_map, free_by_mids, gr_free }, REPEAT_COUNT ); -} - -// Checks converter returns the same mem_id for the same gralloc handle. -TEST(MfxFrameConverter, CacheResources) -{ - const int WIDTH = 1920; - const int HEIGHT = 1080; - const int REPEAT_COUNT = 10; - - auto test_cache = [&] (MfxGrallocAllocator* gr_allocator, MfxFrameAllocator*, - MfxFrameConverter* converter) { - - buffer_handle_t handle {}; - - c2_status_t res = gr_allocator->Alloc(WIDTH, HEIGHT, &handle); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(handle, nullptr); - - mfxMemId mfx_mem_ids[REPEAT_COUNT] {}; - - for (int i = 0; i < REPEAT_COUNT; ++i) { - bool decode_target { false }; - mfxStatus mfx_sts = converter->ConvertGrallocToVa(handle, decode_target, &mfx_mem_ids[i]); - EXPECT_EQ(MFX_ERR_NONE, mfx_sts); - EXPECT_NE(mfx_mem_ids[i], nullptr); - } - - ASSERT_TRUE(REPEAT_COUNT > 1); - for (int i = 1; i < REPEAT_COUNT; ++i) { - EXPECT_EQ(mfx_mem_ids[0], mfx_mem_ids[i]); - } - }; - - MfxFrameConverterTest( { test_cache } ); -} - -typedef std::function MfxFramePoolAllocatorTestStep; - -static void MfxFramePoolAllocatorTest(const std::vector& steps, int repeat_count = 1) -{ - std::shared_ptr c2_allocator; - c2_status_t res = C2_OK; - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, {}, reflector, &res)); - EXPECT_EQ(res, C2_OK); - - res = GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, - component, &c2_allocator); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(c2_allocator, nullptr); - - std::unique_ptr dev { new MfxDevVa(MfxDev::Usage::Decoder) }; - - mfxStatus sts = dev->Init(); - EXPECT_EQ(MFX_ERR_NONE, sts); - - if (c2_allocator) { - std::shared_ptr allocator = dev->GetFrameAllocator(); - EXPECT_NE(allocator, nullptr); - std::shared_ptr pool_allocator = dev->GetFramePoolAllocator(); - EXPECT_NE(pool_allocator, nullptr); - if (pool_allocator) { - - pool_allocator->SetC2Allocator(c2_allocator); - - for (int i = 0; i < repeat_count; ++i) { - for (auto& step : steps) { - step(allocator.get(), pool_allocator.get()); - } - } - } - } - - sts = dev->Close(); - EXPECT_EQ(MFX_ERR_NONE, sts); -} - -// Tests a typical use sequence for MfxFramePoolAllocator. -// 1) Preallocate pool of frames through MfxFrameAllocator::AllocFrames. -// 2) Acquire C2 Graphic Blocks from the allocator, saves C2 handles and -// their wired MFX Mem IDs for future comparison. -// 3) Free C2 Graphic Blocks by releasing their shared_ptrs. -// 4) Acquire C2 Graphic Blocks again, check C2 handles and -// their wired MFX Mem IDs are the same as saved on step 2. -// 5) Reset allocator - release ownership of allocated C2 handles (no allocated any more). -// 6) Allocate again. -// 7) Check all handles are new. -TEST(MfxFramePoolAllocator, RetainHandles) -{ - const size_t FRAME_COUNT = 10; - const int WIDTH = 1920; - const int HEIGHT = 1080; - const mfxU32 FOURCC = MFX_FOURCC_NV12; - std::shared_ptr c2_blocks[FRAME_COUNT]; - - std::map handleC2ToMfx; - - mfxFrameAllocResponse response {}; - - auto mfx_alloc = [&] (MfxFrameAllocator* allocator, MfxFramePoolAllocator*) { - - mfxFrameAllocRequest request {}; - request.Type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; - request.NumFrameMin = FRAME_COUNT; - request.NumFrameSuggested = FRAME_COUNT; - InitFrameInfo(FOURCC, WIDTH, HEIGHT, &request.Info); - - mfxStatus sts = allocator->AllocFrames(&request, &response); - EXPECT_EQ(sts, MFX_ERR_NONE); - EXPECT_EQ(response.NumFrameActual, request.NumFrameMin); - - EXPECT_NE(response.mids, nullptr); - }; - - auto pool_alloc = [&] (MfxFrameAllocator* allocator, MfxFramePoolAllocator* pool_allocator) { - for (size_t i = 0; i < FRAME_COUNT; ++i) { - c2_blocks[i] = pool_allocator->Alloc(); - EXPECT_NE(c2_blocks[i], nullptr); - if (c2_blocks[i]) { - const C2Handle* c2_handle = c2_blocks[i]->handle(); - mfxHDL mfx_handle {}; - mfxStatus sts = allocator->GetFrameHDL(response.mids[i], &mfx_handle); - EXPECT_EQ(sts, MFX_ERR_NONE); - handleC2ToMfx[c2_handle] = mfx_handle; - } - } - EXPECT_EQ(handleC2ToMfx.size(), FRAME_COUNT); - }; - - auto pool_free = [&] (MfxFrameAllocator*, MfxFramePoolAllocator*) { - for (size_t i = 0; i < FRAME_COUNT; ++i) { - c2_blocks[i].reset(); - } - }; - - auto pool_reset = [&] (MfxFrameAllocator*, MfxFramePoolAllocator* pool_allocator) { - pool_allocator->Reset(); - }; - - auto alloc_retains_handles = [&] (MfxFrameAllocator* allocator, MfxFramePoolAllocator* pool_allocator) { - for (size_t i = 0; i < FRAME_COUNT; ++i) { - c2_blocks[i] = pool_allocator->Alloc(); - EXPECT_NE(c2_blocks[i], nullptr); - if (c2_blocks[i]) { - const C2Handle* c2_handle = c2_blocks[i]->handle(); - mfxHDL mfx_handle {}; - mfxStatus sts = allocator->GetFrameHDL(response.mids[i], &mfx_handle); - EXPECT_EQ(sts, MFX_ERR_NONE); - - EXPECT_EQ(handleC2ToMfx[c2_handle], mfx_handle); - } - } - }; - - auto alloc_another_handles = [&] (MfxFrameAllocator*, MfxFramePoolAllocator* pool_allocator) { - std::shared_ptr c2_blocks_2[FRAME_COUNT]; - for (size_t i = 0; i < FRAME_COUNT; ++i) { - c2_blocks_2[i] = pool_allocator->Alloc(); - EXPECT_NE(c2_blocks_2[i], nullptr); - if(c2_blocks_2[i]) { - const C2Handle* c2_handle = c2_blocks_2[i]->handle(); - EXPECT_EQ(handleC2ToMfx.find(c2_handle), handleC2ToMfx.end()); - } - } - }; - - MfxFramePoolAllocatorTest( { mfx_alloc, pool_alloc, pool_free, alloc_retains_handles, - pool_reset, mfx_alloc, alloc_another_handles } ); -} - -#endif - -// Creates 2 graphic blocks, fill 1st with pattern, -// copy contents to 2nd and check pattern there. -TEST(C2Utils, CopyGraphicView) -{ - const int WIDTH = 1920; - const int HEIGHT = 1080; - - do { - c2_status_t res = C2_OK; - - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, {}, reflector, &res)); - std::shared_ptr c2_allocator; - GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &c2_allocator); - EXPECT_NE(c2_allocator, nullptr); - if (!c2_allocator) break; - - std::shared_ptr src_block, dst_block; - - for (auto block : {&src_block, &dst_block}) { - c2_allocator->fetchGraphicBlock(WIDTH, HEIGHT, HAL_PIXEL_FORMAT_NV12_Y_TILED_INTEL, - { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }, block); - EXPECT_NE(*block, nullptr); - } - if (src_block == nullptr || dst_block == nullptr) break; - - std::unique_ptr src_view, dst_view; - - MapGraphicBlock(*src_block, MFX_SECOND_NS, &src_view); - MapGraphicBlock(*dst_block, MFX_SECOND_NS, &dst_view); - if (src_view == nullptr || dst_view == nullptr) break; - - FillFrameContents(WIDTH, HEIGHT, 0/*pattern seed*/, src_view->layout(), src_view->data()); - - res = CopyGraphicView(src_view.get(), dst_view.get()); - EXPECT_EQ(res, C2_OK); - - CheckFrameContents(WIDTH, HEIGHT, 0/*pattern seed*/, dst_view->layout(), dst_view->data()); - - } while(false); -} diff --git a/unittests/src/c2_vndk_test.cpp b/unittests/src/c2_vndk_test.cpp deleted file mode 100755 index 1705ede6..00000000 --- a/unittests/src/c2_vndk_test.cpp +++ /dev/null @@ -1,533 +0,0 @@ -// Copyright (c) 2017-2021 Intel Corporation -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to deal -// in the Software without restriction, including without limitation the rights -// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -// copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in all -// copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -// SOFTWARE. - -/* - * Copyright 2017 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. - */ - -// The contents of this file was copied -// from AOSP frameworks/av/media/libstagefright/codec2/tests/vndk/C2BufferTest.cpp -// and modified then. - -#include -#include "mfx_defs.h" - -#include -#include -#include "mfx_c2_component.h" -#include "mfx_c2_components_registry.h" -#include "mfx_gralloc_allocator.h" - -#include - -using namespace android; - -#define MOCK_COMPONENT_ENC "c2.intel.mock.encoder" -#define MOCK_COMPONENT MOCK_COMPONENT_ENC // use encoder for common tests - -class C2BufferTest -{ -public: - C2BufferTest() = default; - ~C2BufferTest() = default; - - std::shared_ptr makeLinearBlockPool() { - - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, {}, reflector, nullptr)); - - std::shared_ptr block_pool; - GetCodec2BlockPool(C2BlockPool::BASIC_LINEAR, component, &block_pool); - return block_pool; - } - - std::shared_ptr makeGraphicBlockPool() { - - std::shared_ptr reflector = std::make_shared(); - std::shared_ptr component(MfxCreateC2Component(MOCK_COMPONENT, {}, reflector, nullptr)); - - std::shared_ptr block_pool; - GetCodec2BlockPool(C2BlockPool::BASIC_GRAPHIC, component, &block_pool); - return block_pool; - } -}; - -TEST(C2BufferTest, BlockPoolTest) { - constexpr size_t kCapacity = 1024u * 1024u; - - C2BufferTest test; - std::shared_ptr blockPool(test.makeLinearBlockPool()); - - std::shared_ptr block; - ASSERT_EQ(C2_OK, blockPool->fetchLinearBlock( - kCapacity, - { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }, - &block)); - ASSERT_TRUE(block); - - C2Acquirable writeViewHolder = block->map(); - C2WriteView writeView = writeViewHolder.get(); - ASSERT_EQ(C2_OK, writeView.error()); - ASSERT_EQ(kCapacity, writeView.capacity()); - ASSERT_EQ(0u, writeView.offset()); - ASSERT_EQ(kCapacity, writeView.size()); - - uint8_t *data = writeView.data(); - ASSERT_NE(nullptr, data); - for (size_t i = 0; i < writeView.size(); ++i) { - data[i] = i % 100u; - } - - C2Fence fence; - C2ConstLinearBlock constBlock = block->share( - kCapacity / 3, kCapacity / 3, fence); - - C2Acquirable readViewHolder = constBlock.map(); - C2ReadView readView = readViewHolder.get(); - ASSERT_EQ(C2_OK, readView.error()); - ASSERT_EQ(kCapacity / 3, readView.capacity()); - - // TODO: fence - const uint8_t *constData = readView.data(); - ASSERT_NE(nullptr, constData); - for (size_t i = 0; i < readView.capacity(); ++i) { - ASSERT_EQ((i + kCapacity / 3) % 100u, constData[i]) << " at i = " << i - << "; data = " << static_cast(data) - << "; constData = " << static_cast(constData); - } - - readView = readView.subView(333u, 100u); - ASSERT_EQ(C2_OK, readView.error()); - ASSERT_EQ(100u, readView.capacity()); - - constData = readView.data(); - ASSERT_NE(nullptr, constData); - for (size_t i = 0; i < readView.capacity(); ++i) { - ASSERT_EQ((i + 333u + kCapacity / 3) % 100u, constData[i]) << " at i = " << i; - } -} - -void fillPlane(const C2Rect rect, const C2PlaneInfo info, uint8_t *addr, uint8_t value) { - for (uint32_t row = 0; row < rect.height / info.rowSampling; ++row) { - int32_t rowOffset = (row + rect.top / info.rowSampling) * info.rowInc; - for (uint32_t col = 0; col < rect.width / info.colSampling; ++col) { - int32_t colOffset = (col + rect.left / info.colSampling) * info.colInc; - addr[rowOffset + colOffset] = value; - } - } -} - -bool verifyPlane(const C2Rect rect, const C2PlaneInfo info, const uint8_t *addr, uint8_t value) { - for (uint32_t row = 0; row < rect.height / info.rowSampling; ++row) { - int32_t rowOffset = (row + rect.top / info.rowSampling) * info.rowInc; - for (uint32_t col = 0; col < rect.width / info.colSampling; ++col) { - int32_t colOffset = (col + rect.left / info.colSampling) * info.colInc; - if (addr[rowOffset + colOffset] != value) { - return false; - } - } - } - return true; -} - -TEST(C2BufferTest, GraphicBlockPoolTest) { - constexpr uint32_t kWidth = 320; - constexpr uint32_t kHeight = 240; - - C2BufferTest test; - - std::shared_ptr blockPool(test.makeGraphicBlockPool()); - - auto fill_planes = [&] (std::shared_ptr block) { - - C2Acquirable graphicViewHolder = block->map(); - C2GraphicView graphicView = graphicViewHolder.get(); - ASSERT_EQ(C2_OK, graphicView.error()); - ASSERT_EQ(kWidth, graphicView.width()); - ASSERT_EQ(kHeight, graphicView.height()); - - uint8_t *const *data = graphicView.data(); - C2PlanarLayout layout = graphicView.layout(); - ASSERT_NE(nullptr, data); - - uint8_t *y = data[C2PlanarLayout::PLANE_Y]; - C2PlaneInfo yInfo = layout.planes[C2PlanarLayout::PLANE_Y]; - uint8_t *u = data[C2PlanarLayout::PLANE_U]; - C2PlaneInfo uInfo = layout.planes[C2PlanarLayout::PLANE_U]; - uint8_t *v = data[C2PlanarLayout::PLANE_V]; - C2PlaneInfo vInfo = layout.planes[C2PlanarLayout::PLANE_V]; - - fillPlane({ kWidth, kHeight }, yInfo, y, 0); - fillPlane({ kWidth, kHeight }, uInfo, u, 0); - fillPlane({ kWidth, kHeight }, vInfo, v, 0); - fillPlane(C2Rect(kWidth / 2, kHeight / 2).at(kWidth / 4, kHeight / 4), yInfo, y, 0x12); - fillPlane(C2Rect(kWidth / 2, kHeight / 2).at(kWidth / 4, kHeight / 4), uInfo, u, 0x34); - fillPlane(C2Rect(kWidth / 2, kHeight / 2).at(kWidth / 4, kHeight / 4), vInfo, v, 0x56); - }; - - auto verify_planes = [&] (std::shared_ptr block) { - - C2Fence fence; - C2ConstGraphicBlock constBlock = block->share( - { kWidth, kHeight }, fence); - block.reset(); - - C2Acquirable constViewHolder = constBlock.map(); - const C2GraphicView constGraphicView = constViewHolder.get(); - ASSERT_EQ(C2_OK, constGraphicView.error()); - ASSERT_EQ(kWidth, constGraphicView.width()); - ASSERT_EQ(kHeight, constGraphicView.height()); - - const uint8_t *const *constData = constGraphicView.data(); - C2PlanarLayout layout = constGraphicView.layout(); - ASSERT_NE(nullptr, constData); - - const uint8_t *cy = constData[C2PlanarLayout::PLANE_Y]; - C2PlaneInfo yInfo = layout.planes[C2PlanarLayout::PLANE_Y]; - const uint8_t *cu = constData[C2PlanarLayout::PLANE_U]; - C2PlaneInfo uInfo = layout.planes[C2PlanarLayout::PLANE_U]; - const uint8_t *cv = constData[C2PlanarLayout::PLANE_V]; - C2PlaneInfo vInfo = layout.planes[C2PlanarLayout::PLANE_V]; - - ASSERT_TRUE(verifyPlane(C2Rect(kWidth / 2, kHeight / 2).at(kWidth / 4, kHeight / 4), yInfo, cy, 0x12)); - ASSERT_TRUE(verifyPlane(C2Rect(kWidth / 2, kHeight / 2).at(kWidth / 4, kHeight / 4), uInfo, cu, 0x34)); - ASSERT_TRUE(verifyPlane(C2Rect(kWidth / 2, kHeight / 2).at(kWidth / 4, kHeight / 4), vInfo, cv, 0x56)); - ASSERT_TRUE(verifyPlane({ kWidth, kHeight / 4 }, yInfo, cy, 0)); - ASSERT_TRUE(verifyPlane({ kWidth, kHeight / 4 }, uInfo, cu, 0)); - ASSERT_TRUE(verifyPlane({ kWidth, kHeight / 4 }, vInfo, cv, 0)); - ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight }, yInfo, cy, 0)); - ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight }, uInfo, cu, 0)); - ASSERT_TRUE(verifyPlane({ kWidth / 4, kHeight }, vInfo, cv, 0)); - }; - - uint32_t pixel_formats[] { - HAL_PIXEL_FORMAT_YCBCR_420_888, - HAL_PIXEL_FORMAT_NV12_TILED_INTEL - }; - - for (uint32_t pixel_format : pixel_formats ) { - - std::shared_ptr block; - ASSERT_EQ(C2_OK, blockPool->fetchGraphicBlock( - kWidth, - kHeight, - pixel_format, - { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }, - &block)); - ASSERT_TRUE(block); - - fill_planes(block); - verify_planes(std::move(block)); // move to allow verify_planes free block - } -} - -namespace map_test -{ -const uint32_t WIDTH = 176; -const uint32_t HEIGHT = 144; -const uint32_t PIXEL_FORMAT = HAL_PIXEL_FORMAT_NV12_TILED_INTEL; - -// Alloc/Map wrapper for C2 -class C2 -{ -public: - using Block = std::shared_ptr; - using View = C2GraphicView; - - C2() - { - C2BufferTest test; - block_pool_ = test.makeGraphicBlockPool(); - } - - void Alloc(std::list* dst) - { - std::shared_ptr block; - EXPECT_EQ(C2_OK, block_pool_->fetchGraphicBlock(WIDTH, HEIGHT, PIXEL_FORMAT, - { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }, - &block)); - EXPECT_TRUE(block); - if (block) dst->push_back(std::move(block)); - } - - void Map(Block& block, std::list* dst) - { - C2Acquirable acq_graph_view = block->map(); - C2GraphicView view{acq_graph_view.get()}; - EXPECT_EQ(view.error(), C2_OK); - c2_status_t res = C2_OK; //acq_graph_view.wait(MFX_SECOND_NS); wait not supported yet - //EXPECT_EQ(res, C2_OK); - - if (view.error() == C2_OK && res == C2_OK) - dst->emplace_back(view); - } - -private: - std::shared_ptr block_pool_; -}; - -// Alloc/Map wrapper for gralloc -class Gralloc -{ -public: - class Block - { - public: - Block() = default; - MFX_CLASS_NO_COPY(Block); - ~Block() { if (deleter_) deleter_(handle_); } - - private: - buffer_handle_t handle_{}; - std::function deleter_; - friend class Gralloc; - }; - - class View - { - public: - View() = default; - MFX_CLASS_NO_COPY(View); - ~View() { if (deleter_) deleter_(handle_); } - - C2PlanarLayout layout() const { return layout_; } - const uint8_t *const *data() const { return data_; } - - private: - uint8_t* data_[C2PlanarLayout::MAX_NUM_PLANES]{}; - C2PlanarLayout layout_; - buffer_handle_t handle_{}; - std::function deleter_; - friend class Gralloc; - }; - - Gralloc() - { - c2_status_t res = MfxGrallocAllocator::Create(&allocator); - EXPECT_EQ(res, C2_OK); - EXPECT_NE(allocator, nullptr); - } - - void Alloc(std::list* dst) - { - buffer_handle_t handle{}; - EXPECT_EQ(C2_OK, allocator->Alloc(WIDTH, HEIGHT, &handle)); - EXPECT_TRUE(handle); - if (handle) { - dst->emplace_back(); - dst->back().handle_ = handle; - dst->back().deleter_ = [this](buffer_handle_t h) { - c2_status_t res = allocator->Free(h); - EXPECT_EQ(res, C2_OK); - }; - } - } - - void Map(Block& block, std::list* dst) - { - dst->emplace_back(); // construct view in place - - c2_status_t res = allocator->LockFrame(block.handle_, dst->back().data_, &(dst->back().layout_)); - EXPECT_EQ(res, C2_OK); - - if (res == C2_OK) { - dst->back().handle_ = block.handle_; - dst->back().deleter_ = [this](buffer_handle_t h) { - c2_status_t res = allocator->UnlockFrame(h); - EXPECT_EQ(res, C2_OK); - }; - } else { - dst->pop_back(); // remove in case of failure - } - } - -private: - std::unique_ptr allocator; -}; - -} // namespace map_test - -// Test runs multiple threads, allocates graphic blocks there and maps them simultanously. -// Memory regions where blocks are mapped to are checked for mutual intersections. -// The tests came as a result of investigation random DecodeBitExact crashes/output corruptions -// caused by IMapper mapping different blocks into the same memory if called from -// different threads simultanously. -template -void NotOverlappedTest() -{ - const int BLOCK_COUNT = 16; - const int PASS_COUNT = 1000; - const int THREAD_COUNT = 10; - static_assert(THREAD_COUNT >= 2, "Test requires at least 2 threads"); - - std::thread threads[THREAD_COUNT]; - - struct MemoryRegion - { - const uint8_t* start{}; - size_t length{}; - }; - - std::list regions[THREAD_COUNT]; - std::atomic regions_ready_count{}; - std::atomic main_pass_index{}; - std::atomic compare_count{}; - - Allocator allocator; - - auto allocate_blocks = [&]()->std::list { - std::list blocks; - for (int i = 0; i < BLOCK_COUNT; ++i) { - allocator.Alloc(&blocks); - } - return blocks; - }; - - auto map_blocks = [&]( - std::list& blocks)->std::list { - - std::list views; - - for (auto& block : blocks) { - allocator.Map(block, &views); - } - return views; - }; - - auto get_memory_regions = [&](const C2PlanarLayout& layout, const uint8_t *const *data) { - std::list regions; - - struct Offsets - { - ssize_t min{}; - ssize_t max{}; - }; - - std::map root_plane_offsets; - for (uint32_t plane_index = 0; plane_index < layout.numPlanes; ++plane_index) { - const C2PlaneInfo& plane{layout.planes[plane_index]}; - ssize_t min_offset = plane.offset + plane.minOffset(0, 0); - ssize_t max_offset = plane.offset + - plane.maxOffset(map_test::WIDTH / plane.colSampling, map_test::HEIGHT / plane.rowSampling); - auto it = root_plane_offsets.find(plane.rootIx); - if (it != root_plane_offsets.end()) { - Offsets& offsets = it->second; - if (min_offset < offsets.min) offsets.min = min_offset; - if (max_offset > offsets.max) offsets.max = max_offset; - } else { - root_plane_offsets.emplace(plane.rootIx, Offsets({min_offset, max_offset})); - } - } - - for (const auto& item : root_plane_offsets) { - uint32_t root_index = item.first; - const Offsets& offsets = item.second; - - regions.emplace_back( - MemoryRegion{data[root_index] + offsets.min, (size_t)(offsets.max - offsets.min)}); - } - - return regions; - }; - - auto thread_func = [&] (size_t thread_index) { - // allocate BLOCK_COUNT in local storage - std::list blocks = allocate_blocks(); - - for (int pass_index = 1; pass_index <= PASS_COUNT; ++pass_index) { - - while (main_pass_index.load() != pass_index); // wait for iteration start - - // list is outside mapping loop to keep blocks mapped - std::list views = map_blocks(blocks); // map all local blocks - - for (const auto& view : views) { - regions[thread_index].splice(regions[thread_index].end(), - get_memory_regions(view.layout(), view.data())); - } - - // signal memory list is ready - ++regions_ready_count; - - // wait for comparison - while (compare_count.load() != pass_index); - // unmap is done automatically by views destruction - } - }; - - for (int i = 0; i < THREAD_COUNT; ++i) { - threads[i] = std::thread(thread_func, i); - } - - for (int i = 0; i < PASS_COUNT; ++i) { - - ++main_pass_index; // signal of iteration start - - // wait for lists are ready - while (regions_ready_count.load() != THREAD_COUNT); - - // check memory region lists for intersections - std::list all_regions; // check every region against this list and insert in there - for (int j = 0; j < THREAD_COUNT; ++j) { - for (const MemoryRegion& region : regions[j]) { - auto intersects = [®ion] (const MemoryRegion& other)->bool { - return (region.start < other.start + other.length) && - (other.start < region.start + region.length); - }; - EXPECT_EQ(std::find_if(all_regions.begin(), all_regions.end(), intersects), all_regions.end()); - all_regions.push_back(region); - } - regions[j].clear(); - } - - regions_ready_count.store(0); - - ++compare_count; // signal comparison is done - } - - // join threads - for (int i = 0; i < THREAD_COUNT; ++i) { - threads[i].join(); - } -} - -// Tests overlapping for C2GraphicBlock. -TEST(C2BufferTest, C2GraphicMappingNotOverlappedTest) -{ - NotOverlappedTest(); -} - -// Tests overlapping for gralloc handles. -TEST(C2BufferTest, GrallocMappingNotOverlappedTest) -{ - NotOverlappedTest(); -}