[GStreamer] 插件编写

Tips:

A plugin registry is used to store the details of the plugins in a binary registry file. 
This way, a program using GStreamer does not have to load all plugins to determine which are needed. 
Plugins are only loaded when their provided elements are requested.


All streams of data in GStreamer are chopped up into chunks that are passed from a source pad on one element to a sink pad on another element. 
GstMiniObject is the structure used to hold these chunks of data.


GstMiniObject contains the following important types:
    1.An exact type indicating what type of data (event, buffer, ...) this GstMiniObject is.
    2.A reference count indicating the number of elements currently holding a reference to the miniobject. When the reference count falls to zero, 
        the miniobject will be disposed, and its memory will be freed in some sense (see below for more details).
        
Events may contain several of the following items:
    1.A subtype indicating the type of the contained event.
    2.The other contents of the event depend on the specific event type.
    
    
    
    GstMiniObject, GstBuffer and GstEvent.
    
    
Another way an element might get specialized buffers is to request them from a downstream peer through a GstBufferPool or GstAllocator.


Media types and Properties
GStreamer uses a type system to ensure that the data passed between elements is in a recognized format. 
The type system is also important for ensuring that the parameters required to fully specify a format match up correctly when linking pads between elements. 
Each link that is made between elements has a specified type and optionally a set of properties. See more about caps negotiation in Caps negotiation.

Demo:

MyFilter.h

#pragma once

#include <gst/gst.h>

#define VERSION "1.0"
#define PACKAGE "myfilter"


G_BEGIN_DECLS

//property id
enum{
	PROP_0,			//id = 0
	PROP_SILENT,	//1
	//PROP_A,		//2
	//PROP_B		//3
	PROP_CNT		//4
};

//instance struct, every instance has one
typedef struct _GstMyFilter 
{
	GstElement element;

	//pads
	GstPad* srcpad;		//src pad
	GstPad* sinkpad;		//sink pad

	gboolean silent;

}GstMyFilter;


//class struct, all instance shared content
typedef struct _GstMyFilterClass 
{
	GstElementClass parent_class;
}GstMyFilterClass;

/* Standard macros for defining types for this element.  */
#define GST_TYPE_MY_FILTER (gst_my_filter_get_type())
#define GST_MY_FILTER(obj) \
  (G_TYPE_CHECK_INSTANCE_CAST((obj),GST_TYPE_MY_FILTER,GstMyFilter))
#define GST_MY_FILTER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_CAST((klass),GST_TYPE_MY_FILTER,GstMyFilterClass))
#define GST_IS_MY_FILTER(obj) \
  (G_TYPE_CHECK_INSTANCE_TYPE((obj),GST_TYPE_MY_FILTER))
#define GST_IS_MY_FILTER_CLASS(klass) \
  (G_TYPE_CHECK_CLASS_TYPE((klass),GST_TYPE_MY_FILTER))

/* Standard function returning type information. */
GType gst_my_filter_get_type(void);

G_END_DECLS

//GST_ELEMENT_REGISTER_DECLARE (my_filter)

MyFilter.c

#include "MyFilter.h"
#include <gst/gstelement.h>
#include <gst/audio/audio-format.h>
#include <gst/gstpad.h>
#include <gst/gstplugin.h>
#include <gst/gstconfig.h>
#include <gobject/gparam.h>

G_DEFINE_TYPE(GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT);
//GST_ELEMENT_REGISTER_DEFINE(my_filter, "my-filter", GST_RANK_NONE, GST_TYPE_MY_FILTER);


//pad模板准备,描述pad,之后会在_class_init中注册 sink_factory 和 src_factory,然后在_init中创建实例
//sink pad
static GstStaticPadTemplate sink_factory = GST_STATIC_PAD_TEMPLATE(
	"sink",						//short name of pad
	GST_PAD_SINK,				//pad direction - sink - input
	GST_PAD_ALWAYS,				//existence - always
	GST_STATIC_CAPS("ANY")		//support data type - any
);
static GstStaticPadTemplate src_factory = GST_STATIC_PAD_TEMPLATE(
	"src",						//short name of pad
	GST_PAD_SRC,				//pad direction - src - output
	GST_PAD_ALWAYS,				//existence - always
	GST_STATIC_CAPS("ANY")		//support data type - any
);
static GstStaticPadTemplate sink_factory_1 = GST_STATIC_PAD_TEMPLATE(
	"sink_%u",
	GST_PAD_SINK,
	GST_PAD_ALWAYS,
	GST_STATIC_CAPS(
		"audio/x-raw, "
		"format = (string) " GST_AUDIO_NE(S16) ", "
		"channels = (int) { 1, 2 }, "
		"rate = (int) [ 8000, 96000 ]"
	)
);

// ================ inner function start ================
// ================ inner function start ================
// ================ inner function start ================

static GstBuffer *gst_my_filter_process_data(GstMyFilter* filter,GstBuffer* buf) 
{
	//申请新内存
	//处理buf
	//获得新数据写入新内存
	//返回
	return NULL;
}

static gboolean gst_my_filter_stop_processing(GstMyFilter* filter)
{
	//让当前element停止工作
	return TRUE;
}

static gboolean gst_my_filter_allocate_memory(GstMyFilter* filter)
{
	//申请必要的内存
	//加载必要的run-time lib
	return TRUE;
}

static gboolean gst_my_filter_free_memory(GstMyFilter* filter)
{
	//释放内存
	//卸载run-time lib
	return TRUE;
}

// ================ inner function end ================
// ================ inner function end ================
// ================ inner function end ================

//(!!!) sink pad 常规数据处理函数
static GstFlowReturn gst_my_filter_chain(GstPad* pad/*pad reveive buffer*/ , GstObject *parent, GstBuffer *buf/*to be precessed*/)
{
	GstMyFilter *filter = GST_MY_FILTER(parent);	//可以理解为获取this指针

	GstBuffer *outbuf;

	outbuf = gst_my_filter_process_data(filter, buf);
	gst_buffer_unref(buf);
	if (!outbuf) {
		/* something went wrong - signal an error */
		GST_ELEMENT_ERROR(GST_ELEMENT(filter), STREAM, FAILED, (NULL), (NULL));
		return GST_FLOW_ERROR;
	}

	return gst_pad_push(filter->srcpad, outbuf);
}

//(!!!) sink pad event数据处理
static gboolean gst_my_filter_event(GstPad* pad/*pad reveive event*/, GstObject *parent, GstEvent *event/*to be preocessed*/)
{
	gboolean ret;
	GstMyFilter *filter = GST_MY_FILTER(parent);	//可以理解为获取this指针
	
	switch (GST_EVENT_TYPE(event)) {
	case GST_EVENT_CAPS:
		ret = gst_pad_push_event(filter->srcpad, event);	/* push the event downstream */
		break;
	case GST_EVENT_EOS:
		gst_my_filter_stop_processing(filter);
		ret = gst_pad_event_default(pad, parent, event);	//目前只有GST_EVENT_CAPS有默认动作
		break;
	default:
		ret = gst_pad_event_default(pad, parent, event);	//目前只有GST_EVENT_CAPS有默认动作
		break;
	}

	return ret;
}

//(!!!) query 对象处理
static gboolean gst_my_filter_src_query(GstPad* pad, GstObject* parent, GstQuery* query)
{
	gboolean ret;
	GstMyFilter *filter = GST_MY_FILTER(parent);

	switch (GST_QUERY_TYPE(query)) {
	case GST_QUERY_POSITION:
		/* we should report the current position */
		break;
	case GST_QUERY_DURATION:
		/* we should report the duration here */
		break;
	case GST_QUERY_CAPS:
		/* we should report the supported caps here */
		break;
	default:
		/* just call the default handler */
		ret = gst_pad_query_default(pad, parent, query);		//默认handler
		break;
	}
	return ret;
}

//(!!!) state changed 处理函数
static GstStateChangeReturn gst_my_filter_change_state(GstElement *element, GstStateChange transition)
{
	GstStateChangeReturn ret = GST_STATE_CHANGE_SUCCESS;
	GstMyFilter *filter = GST_MY_FILTER(element);
	
	/*
		Note that upwards (NULL=>READY, READY=>PAUSED, PAUSED=>PLAYING) and downwards (PLAYING=>PAUSED, PAUSED=>READY, READY=>NULL) state changes 
		are handled in two separate blocks with the downwards state change handled only after we have chained up to the parent class's state change function. 
		This is necessary in order to safely handle concurrent access by multiple threads.
	*/

	//process upwards state change
	switch (transition) {
	case GST_STATE_CHANGE_NULL_TO_READY:
		if (!gst_my_filter_allocate_memory(filter))	//申请资源
			return GST_STATE_CHANGE_FAILURE;
		break;
	case GST_STATE_CHANGE_READY_TO_PAUSED:
		//dosomething
		break;
	case GST_STATE_CHANGE_PAUSED_TO_PLAYING:
		//dosomething
		break;
	default:
		break;
	}

	//唤起parent class 的 state change 处理函数
	//ret = GST_ELEMENT_CLASS(parent_class)->change_state(element, transition);
	ret = GST_ELEMENT_CLASS(GST_ELEMENT_GET_CLASS(element))->change_state(element, transition);
	if (ret == GST_STATE_CHANGE_FAILURE)
		return ret;

	//process downwards state change
	switch (transition) {
	case GST_STATE_CHANGE_PLAYING_TO_PAUSED:
		//dosomething
		break;
	case GST_STATE_CHANGE_PAUSED_TO_READY:
		//dosomething
		break;
	case GST_STATE_CHANGE_READY_TO_NULL:
		gst_my_filter_free_memory(filter);
		break;
	default:
		break;
	}

	return ret;
}

//(!!!) property 读写处理函数
static void gst_my_filter_set_property(GObject* object, guint prop_id, const GValue * value, GParamSpec *pspec)
{
	GstMyFilter* filter = GST_MY_FILTER(object);

	switch (prop_id) {
	case PROP_SILENT:
		filter->silent = g_value_get_boolean(value);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}
static void gst_my_filter_get_property(GObject* object, guint prop_id, GValue * value, GParamSpec *pspec)
{
	GstMyFilter *filter = GST_MY_FILTER(object);

	switch (prop_id) {
	case PROP_SILENT:
		g_value_set_boolean(value, filter->silent);
		break;
	default:
		G_OBJECT_WARN_INVALID_PROPERTY_ID(object, prop_id, pspec);
		break;
	}
}



//类初始化函数(类构造函数)
static void gst_my_filter_class_init(GstMyFilterClass *klass)
{
	GstElementClass *element_class = GST_ELEMENT_CLASS(klass);	//for others
	GObjectClass *object_class = G_OBJECT_CLASS(klass);			//for property setter and getter,因为用到的是glib的功能,所以转为gobjectclass

	//meta data
	gst_element_class_set_static_metadata(element_class,
		"obentul example plugin",
		"Example/FirstExample",
		"Shows the basic structure of a plugin",
		"obentul <ykun089@163.com>");

	//register state change funcution
	element_class->change_state = gst_my_filter_change_state;

	//register property setter and getter
	object_class->set_property = gst_my_filter_set_property;
	object_class->get_property = gst_my_filter_get_property;

	//[todo]reigster properties
	//silent 
	//g_object_class_install_property(
	//	object_class, 
	//	PROP_SILENT,
	//	g_param_spec_boolean("property-name: silent", "description", "test propery,no specific function", FALSE, (GParamFlags)(G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS))
	//);

	//register pads
	gst_element_class_add_pad_template(element_class,
		gst_static_pad_template_get(&src_factory));
	gst_element_class_add_pad_template(element_class,
		gst_static_pad_template_get(&sink_factory));
}

//实例初始化函数(实例构造函数)
static void gst_my_filter_init(GstMyFilter *filter)
{
	//instantiates and assigns pads
	filter->srcpad = gst_pad_new_from_static_template(&src_factory,"src_%u");	//%u是不是代表可以有多个?
	filter->sinkpad = gst_pad_new_from_static_template(&sink_factory, "sink_%u");

	//add pads to element
	gst_element_add_pad(GST_ELEMENT(filter),filter->srcpad);
	gst_element_add_pad(GST_ELEMENT(filter), filter->sinkpad);

	//set chain function for sink pad
	gst_pad_set_chain_function(filter->sinkpad, gst_my_filter_chain);

	//set event function
	gst_pad_set_event_function(filter->sinkpad, gst_my_filter_event);

	//set query function
	gst_pad_set_query_function(filter->srcpad, gst_my_filter_src_query);

	//所有属性当前暂不可用
	filter->silent = FALSE;
}

//插件加载时被调用的函数
gboolean plugin_init(GstPlugin* myfilter)
{
	return gst_element_register(myfilter, "MyFilter", GST_RANK_MARGINAL, GST_TYPE_MY_FILTER);
}

//插件描述信息
GST_PLUGIN_DEFINE(
	GST_VERSION_MAJOR,
	GST_VERSION_MINOR,
	my_filter,
	"My filter plugin",
	plugin_init,
	VERSION,
	"LGPL",
	"GStreamer template Plug-ins",
	"http://gstreamer.net/"
)

编译:

安装开发库:

sudo apt install gstreamer*

生成动态库:

gcc -fPIC -shared -I/usr/include/gstreamer-1.0 -I/usr/include/glib-2.0 -I/usr/lib/x86_64-linux-gnu/glib-2.0/include MyFilter.c -o libmy_filter.so

使用gst-inspect查看插件信息:

带详细日志:

gst-inspect --gst-debug-level=9 --plugin libmy_filter.so

简单查看:

gst-inspect --plugin libmy_filter.so

使用gst-inspect查看element信息:

需要先把插件放到gst-inspect的搜索路径下,比如/usr/lib/x86_64-linux-gnu/gstreamer-1.0

查看信息:

gst-inspect MyFilter

注意:

  • 上面所有函数中的xxx 都是 G_DEFINE_TYPE 的第二个参数,如果匹配不上的话编译时会报错。比如如果G_DEFINE_TYPE(MyFilter, my_filter , GST_ELEMENT_TYPE) , 那么 myfilter_chain 就是不合法的。必须写成my_filter_chain。
  • 插件名称需要和G_DEFINE_TYPE的第二个参数匹配。否则报错“Could not load plugin file: File ”./libmyfilter.so“ is not a GStreamer plugin”。将插件名称改为libmy_filter.so即可。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值