Gstreamer作为一个音视频开发的开源框架,应用得非常广泛。Gstreamermm是gstreamer的c++封装,结合glibmm和libsigc++等库,大大简化了gstreamer的开发。但奇怪网上关于gstreamermm的文章和教程凤毛菱角,要想深入掌握使用该库,只有以我有限的水平阅读源码,磕磕碰碰,总算有一点心得。现记录一下在该框架下实现一个gstreamer的filter插件,并添加属性的过程
从BaseTransform类构建插件
直接上代码
#include<iostream>
#include <gstreamermm.h>
#include <gstreamermm/private/basetransform_p.h>
#include<glibmm/main.h>
#include <vector>
#include<opencv2/opencv.hpp>
using namespace Gst;
using namespace std;
class DerivedFromBaseTransform : public Gst::BaseTransform
{
public:
static void class_init(Gst::ElementClass<DerivedFromBaseTransform> *klass) {
klass->set_metadata("derivedfrombasetransform_longname",
"derivedfrombasetransform_classification", "derivedfrombasetransform_detail_description",
"derivedfrombasetransform_detail_author");
klass->add_pad_template(Gst::PadTemplate::create("sink", Gst::PAD_SINK, Gst::PAD_ALWAYS,
Gst::Caps::create_from_string("video/x-raw, format=BGRA")));
klass->add_pad_template(Gst::PadTemplate::create("src", Gst::PAD_SRC, Gst::PAD_ALWAYS,
Gst::Caps::create_from_string("video/x-raw, format=BGRA")));
}
Glib::RefPtr<Gst::Pad> source;
Glib::RefPtr<Gst::Buffer> wbuf;
explicit DerivedFromBaseTransform(GstBaseTransform *gobj)
: Glib::ObjectBase(typeid (DerivedFromBaseTransform)), // type must be registered before use
Gst::BaseTransform(gobj) {
set_passthrough(true);
//set_in_place(true);
//source = get_static_pad("src");
}
Gst::FlowReturn transform_ip_vfunc(const Glib::RefPtr<Gst::Buffer> &buf) override {
//cout << "Hello buffer" << endl;
Gst::MapInfo mapinfo;
buf->map(mapinfo, Gst::MAP_READ);
//wbuf = buf->create_writable();
//wbuf->map(mapinfo, Gst::MAP_WRITE);
//cout << mapinfo.get_size() / 320 / 240 << endl;
cv::Mat mat{240, 320, CV_8UC4, mapinfo.get_data()};
cv::circle(mat, {100, 100}, 30, {0,0,255,255}, 3);
buf->unmap(mapinfo);
//source->push(move(wbuf));
return Gst::FLOW_OK;
}
static bool register_element(Glib::RefPtr<Gst::Plugin> plugin) {
return Gst::ElementFactory::register_element(plugin, "myfilter",
10, Gst::register_mm_type<DerivedFromBaseTransform>("myfilter"));
}
};
main函数
int main() {
Gst::init();
bool plugin_registered = Plugin::register_static(GST_VERSION_MAJOR, GST_VERSION_MINOR,
"derivedfrombasetransform", "exemplary element C++-derived from Gst::BaseTransform",
sigc::ptr_fun(&DerivedFromBaseTransform::register_element), "0.123", "LGPL",
"source?", "package?", "http://example.com");
auto element = Gst::ElementFactory::create_element("myfilter");
auto loop = Glib::MainLoop::create();
auto pipe = Gst::Pipeline::create();
auto queue1 = Gst::ElementFactory::create_element("queue2");
auto queue2 = Gst::ElementFactory::create_element("queue2");
auto src = Gst::ElementFactory::create_element("videotestsrc");
auto out = Gst::ElementFactory::create_element("autovideosink");
auto conv = Gst::ElementFactory::create_element("videoconvert");
auto conv2 = Gst::ElementFactory::create_element("videoconvert");
pipe->add(src)->add(element)->add(conv)->add(out)->add(conv2)->add(queue1)->add(queue2);
src->link(conv)->link(queue1)->link(element)->link(queue2)->link(conv2)->link(out);
pipe->set_state(Gst::STATE_PLAYING);
loop->run();
pipe->set_state(Gst::STATE_NULL);
}
添加属性
如果要为插件添加属性,并接受set_property对属性进行修改,可以在插件中采取下面的方法
#include<iostream>
#include <gstreamermm.h>
#include <gstreamermm/private/basetransform_p.h>
#include<glibmm/main.h>
#include <vector>
#include<opencv2/opencv.hpp>
using namespace Gst;
using namespace std;
// 默认颜色是黑色
#define DEFAULT_COLOR cv::Scalar{0,0,0,255}
class DerivedFromBaseTransform : public Gst::BaseTransform
{
Glib::Property<Glib::ustring> color; //这里定义一个color属性,字符串类型,格式为#RRGGBB
private:
cv::Scalar m_color{DEFAULT_COLOR}; //这里定义将color属性转化成cv::Scalar颜色值的私有变量
// 定义一个私有函数,将颜色属性的#RRGGBB格式的字符串转化为cv::Scalar,保存在m_color中
void convertColor() {
Glib::ustring colorCode = color.get_value();
g_print("You want to change the color to %s. String size: %d\n", colorCode.c_str(), colorCode.size());
if (colorCode.size() != 7 || colorCode[0] != '#'){
g_printerr("Error format of color property: %s\n. It must be in #RRBBGG format", colorCode.c_str());
m_color = DEFAULT_COLOR;
return;
}
// Parse each component of the color code
std::string redCode = colorCode.substr(1, 2);
std::string greenCode = colorCode.substr(3, 2);
std::string blueCode = colorCode.substr(5, 2);
// Convert the color code components from hex to decimal
int red, green, blue;
try{
red = stoi(redCode, nullptr, 16);
green = stoi(greenCode, nullptr, 16);
blue = stoi(blueCode, nullptr, 16);
m_color = cv::Scalar(blue, green, red, 255);
}catch(std::invalid_argument const& e){
g_printerr("Failed to convert the string to number. Use the default #000000 instead\n");
m_color = DEFAULT_COLOR;
}
}
public:
static void class_init(Gst::ElementClass<DerivedFromBaseTransform> *klass) {
klass->set_metadata("derivedfrombasetransform_longname",
"derivedfrombasetransform_classification", "derivedfrombasetransform_detail_description",
"derivedfrombasetransform_detail_author");
klass->add_pad_template(Gst::PadTemplate::create("sink", Gst::PAD_SINK, Gst::PAD_ALWAYS,
Gst::Caps::create_from_string("video/x-raw, format=BGRA")));
klass->add_pad_template(Gst::PadTemplate::create("src", Gst::PAD_SRC, Gst::PAD_ALWAYS,
Gst::Caps::create_from_string("video/x-raw, format=BGRA")));
}
Glib::RefPtr<Gst::Pad> source;
Glib::RefPtr<Gst::Buffer> wbuf;
explicit DerivedFromBaseTransform(GstBaseTransform *gobj)
: Glib::ObjectBase(typeid (DerivedFromBaseTransform)), // type must be registered before use
Gst::BaseTransform(gobj) ,
color(*this, "color", "#00FF00") // 在构造函数中初始化该属性的值为绿色
{
set_passthrough(true);
//set_in_place(true);
//source = get_static_pad("src");
convertColor(); // 将缺省color属性转化为cv::Scalar
// 关键是下面这行,将修改属性的的信号signal_changed链接到本类的convertColor函数,实现set_property的响应
// 试了好久才试出来是这样的方式连接。但有个小问题,就是运行过程中会出现
// GStreamer-CRITICAL **: 18:15:19.763: The created element should be floating, this is probably caused by faulty bindings
// 告警,并且关闭显示窗口后,管道不能自己终结,非要按CTRL-C才能终止程序运行,暂时不知道怎么解决
color.get_proxy().signal_changed().connect(sigc::mem_fun(*this, &DerivedFromBaseTransform::convertColor));
}
Gst::FlowReturn transform_ip_vfunc(const Glib::RefPtr<Gst::Buffer> &buf) override {
//cout << "Hello buffer" << endl;
Gst::MapInfo mapinfo;
buf->map(mapinfo, Gst::MAP_READ);
//wbuf = buf->create_writable();
//wbuf->map(mapinfo, Gst::MAP_WRITE);
//cout << mapinfo.get_size() / 320 / 240 << endl;
// 使用属性中的颜色画圆
cv::Mat mat{240, 320, CV_8UC4, mapinfo.get_data()};
cv::circle(mat, {100, 100}, 30, m_color, 3);
buf->unmap(mapinfo);
//source->push(move(wbuf));
return Gst::FLOW_OK;
}
static bool register_element(Glib::RefPtr<Gst::Plugin> plugin) {
return Gst::ElementFactory::register_element(plugin, "myfilter",
10, Gst::register_mm_type<DerivedFromBaseTransform>("myfilter"));
}
};
// main函数
Gst::init();
bool plugin_registered = Plugin::register_static(GST_VERSION_MAJOR, GST_VERSION_MINOR,
"derivedfrombasetransform", "exemplary element C++-derived from Gst::BaseTransform",
sigc::ptr_fun(&DerivedFromBaseTransform::register_element), "0.123", "LGPL",
"source?", "package?", "http://example.com");
auto loop = Glib::MainLoop::create();
auto pipe = Gst::Pipeline::create();
auto element = Gst::ElementFactory::create_element("myfilter");
auto queue1 = Gst::ElementFactory::create_element("queue2");
auto queue2 = Gst::ElementFactory::create_element("queue2");
auto src = Gst::ElementFactory::create_element("videotestsrc");
auto out = Gst::ElementFactory::create_element("autovideosink");
auto conv = Gst::ElementFactory::create_element("videoconvert");
auto conv2 = Gst::ElementFactory::create_element("videoconvert");
// 这里设置属性
element->set_property("color", Glib::ustring("#0000FF"));
pipe->add(src)->add(element)->add(conv)->add(out)->add(conv2)->add(queue1)->add(queue2);
src->link(conv)->link(queue1)->link(element)->link(queue2)->link(conv2)->link(out);
pipe->set_state(Gst::STATE_PLAYING);
loop->run();
pipe->set_state(Gst::STATE_NULL);