* -------------------------------------------------------------------------
* This file is part of the Vision SDK project.
* Copyright (c) 2025 Huawei Technologies Co.,Ltd.
*
* Vision SDK is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
* Description: The PluginTestHelper For Test.
* Author: MindX SDK
* Create: 2021
* History: NA
*/
#ifndef ATLASINDUSTRYSDK_GSTPLUGINHELPER_H
#define ATLASINDUSTRYSDK_GSTPLUGINHELPER_H
#include <vector>
#include <string>
#include <map>
#include <algorithm>
#include "gst/gst.h"
#include "MxBase/Utils/FileUtils.h"
#include "MxTools/PluginToolkit/base/MxGstBase.h"
#include "MxTools/PluginToolkit/buffer/MxpiBufferManager.h"
#include "MxTools/PluginToolkit/base/MxpiBufferDump.h"
#include "MxpiCommon/DumpDataHelper.h"
using namespace MxBase;
using namespace MxTools;
namespace MxPlugins {
#define ExportPluginRegister(name) extern "C" { void gst_plugin_##name##_register (void); }
#define PluginRegister(name) gst_plugin_##name##_register()
const std::string FAKE_SINK_PLUGIN_NAME = "fakesink";
const std::string SINK_TEXT = "sink";
class PluginTestHelper {
public:
template<typename T>
static T* GetPluginInstance(const std::string& pluginName,
const std::map<std::string, std::string>& properties = {}, size_t sinkPadCount = 0)
{
pluginElement_ = gst_element_factory_make(pluginName.c_str(), nullptr);
if (GST_MXBASE(pluginElement_)->pluginInstance == nullptr) {
std::cout << "gst_element_factory_make failed. pluginName(" << pluginName << ")." << std::endl;
return nullptr;
}
for (size_t i = 0; i < sinkPadCount; ++i) {
if (!DynamicCreateSinkPad()) {
return nullptr;
}
}
for (auto& property : properties) {
SetPluginProperty(property.first, property.second);
}
auto t = (T *)(GST_MXBASE(pluginElement_)->pluginInstance);
gst_element_set_state(pluginElement_, GST_STATE_PLAYING);
auto fakeSinkElement = gst_element_factory_make(FAKE_SINK_PLUGIN_NAME.c_str(), nullptr);
gst_element_set_state(fakeSinkElement, GST_STATE_PLAYING);
auto fakeSinkPad = gst_element_get_static_pad(fakeSinkElement, SINK_TEXT.c_str());
for (auto& srcPad : ((MxGstBase*)pluginElement_)->srcPadVec) {
gst_pad_link(srcPad, fakeSinkPad);
}
gst_pad_set_chain_function_full(fakeSinkPad, ChainCallback, nullptr, nullptr);
return t;
}
static void GetMxpiBufferFromFiles(const std::vector<std::string>& fileNameVec, std::vector<MxpiBuffer*>& result)
{
for (auto& fileName : fileNameVec) {
if (fileName.empty()) {
result.push_back(nullptr);
continue;
}
std::string jsonString = FileUtils::ReadFileContent(fileName);
InputParam inputParam;
inputParam.dataSize = jsonString.size();
inputParam.ptrData = (void*) jsonString.c_str();
auto resultBuffer = MxpiBufferManager::CreateHostBufferAndCopyData(inputParam);
auto buffer = MxpiBufferDump::DoLoad(*resultBuffer);
result.push_back(buffer);
}
}
static MxpiBuffer* CopyBuffer(MxpiBuffer& mxpiBuffer)
{
auto result = new (std::nothrow) MxpiBuffer;
if (result == nullptr) {
LogError << "\"result\" out of memory." << GetErrorInfo(APP_ERR_COMM_OUT_OF_MEM);
return nullptr;
}
gst_buffer_ref((GstBuffer*)mxpiBuffer.buffer);
result->buffer = mxpiBuffer.buffer;
return result;
}
static bool CheckResult(const std::vector<std::string>& fileNameVec)
{
std::cout << "begin to Check Result. size(" << gstBufferVec_.size() << ")." << std::endl;
for (size_t i = 0; i < gstBufferVec_.size(); ++i) {
MxpiBuffer mxpiBuffer{gstBufferVec_[i]};
std::string jsonString = MxpiBufferDump::DoDump(mxpiBuffer, {}, {});
FileUtils::WriteFileContent("./output_" + std::to_string(i) + ".json", jsonString);
EXPECT_TRUE(DumpDataHelper::CompareDumpData(jsonString, FileUtils::ReadFileContent(fileNameVec[0])));
}
std::cout << "end to Check Result" << std::endl;
return !gstBufferVec_.empty();
}
static bool CheckResult(const std::string& fileName, int index)
{
std::cout << "begin to Check Result. size(" << gstBufferVec_.size() << ")." << std::endl;
if (index >= (int)gstBufferVec_.size()) {
return false;
}
MxpiBuffer mxpiBuffer{gstBufferVec_[index]};
std::string jsonString = MxpiBufferDump::DoDump(mxpiBuffer, {}, {});
FileUtils::WriteFileContent("./output_" + std::to_string(index) + ".json", jsonString);
EXPECT_TRUE(DumpDataHelper::CompareDumpData(jsonString, FileUtils::ReadFileContent(fileName)));
std::cout << "end to Check Result" << std::endl;
return !gstBufferVec_.empty();
}
static bool CheckResult(const std::vector<std::string>& fileNameVec, std::vector<std::string> metaDataKeys)
{
std::cout << "begin to Check Result. size(" << gstBufferVec_.size() << ")." << std::endl;
for (size_t i = 0; i < gstBufferVec_.size(); ++i) {
MxpiBuffer mxpiBuffer{gstBufferVec_[i]};
std::string jsonString = MxpiBufferDump::DoDump(mxpiBuffer, {}, {});
FileUtils::WriteFileContent("./output_" + std::to_string(i) + ".json", jsonString);
EXPECT_TRUE(DumpDataHelper::CompareDumpData(jsonString, FileUtils::ReadFileContent(fileNameVec[i]), metaDataKeys));
}
std::cout << "end to Check Result" << std::endl;
return !gstBufferVec_.empty();
}
private:
static bool DynamicCreateSinkPad()
{
auto sinkPadTemplate = gst_element_class_get_pad_template(GST_ELEMENT_GET_CLASS (pluginElement_), "sink_%u");
if (sinkPadTemplate == nullptr) {
std::cout << "pad template is nullptr" << std::endl;
return false;
}
auto pluginSinkPad = gst_element_request_pad(pluginElement_, sinkPadTemplate, NULL, NULL);
if (pluginSinkPad == nullptr) {
std::cout << "pad request is nullptr" << std::endl;
return false;
}
return true;
}
static bool SetPluginProperty(const std::string& propertyName, const std::string& propertyValue)
{
bool result = false;
if (propertyName.empty() || propertyValue.empty()) {
return result;
}
GParamSpec* spec = g_object_class_find_property(G_OBJECT_GET_CLASS(G_OBJECT(pluginElement_)),
propertyName.c_str());
if (G_LIKELY(spec && (spec->flags & G_PARAM_WRITABLE))) {
GValue v = {
0,
};
g_value_init(&v, spec->value_type);
if (!gst_value_deserialize(&v, propertyValue.c_str())) {
if (G_VALUE_HOLDS_STRING(&v)) {
g_value_set_string(&v, propertyValue.c_str());
g_object_set_property(G_OBJECT(pluginElement_), propertyName.c_str(), &v);
result = true;
}
} else {
g_object_set_property(G_OBJECT(pluginElement_), propertyName.c_str(), &v);
result = true;
}
g_value_unset(&v);
}
return result;
}
static GstFlowReturn ChainCallback(GstPad* pad, GstObject* parent, GstBuffer* buffer)
{
if (buffer == nullptr) {
return GST_FLOW_OK;
}
if (std::find(gstBufferVec_.begin(), gstBufferVec_.end(), buffer) == gstBufferVec_.end()) {
gstBufferVec_.push_back(buffer);
std::cout << "current GstBuffer size(" << gstBufferVec_.size() << ")." << std::endl;
} else {
std::cout << "find same buffer." << std::endl;
}
return GST_FLOW_OK;
}
public:
static std::vector<GstBuffer*> gstBufferVec_;
static GstElement* pluginElement_;
};
std::vector<GstBuffer*> PluginTestHelper::gstBufferVec_ = {};
GstElement* PluginTestHelper::pluginElement_ = nullptr;
}
#endif