GStreamer类型系统
Gstreamer的类型系统来源于GLib Object System,GLib 对基本类型进行了重新映射,同时实现了如Lists,hash表等等算法,它比Gobject的类型更通用。Gobject和它的底层类型系统Gtype被用在 GTK+(图形用户界面工具包)和大多数GNOME 库(Linux上的图形桌面环境库)。Gtype用C的方式实现了面向对象,用来兼容不同类型的语言和提供透明的跨语言交叉访问,比如Python和C程序的互相访问。
开发者用定义在gtype.h 文件中的g_type_register_static(), g_type_register_dynamic() 和
g_type_register_fundamental() 来注册一个新一Gtype类型到类型系统中。一般不需要用g_type_register_fundamental() 函数来注册基本类型到类型系统。
Fundamental type基类型是最顶层的类型,不从任何类型派生而来,是类型系统的核心,其他类型则从基类型派生,GtypeFundamentalFlags用来描述基类型的特性,注册为class且为可以实例化的Type极其像object。
Gstreamer的元素类型
GStreamer提供了类型及其参数的定义框架,但是对于类型的含义,需要element的开发者去定义。类型定义的策略如下:
- 如果已有满足需求的类型存在,则不要创建新的Type。
- 创建新的类型前需要就类型参数等等和其他GStreamer开发者进行沟通,交流。
- 新的Type名字不能与现存的的类型名冲突,并且类型名最好能够体现类型细节,不要取"audio/compressed"这类比较通用的类型名字,因为较为通用的名字已经存在的可能性较大,容易和现存类型冲突。
- 描述清楚新创建的类型并加它加入到已知类型中方便其他开发者使用。
- 不推荐创建和其他系统相同类型名但含义不同的类型。
GStreamer类型定义遵循IANA标准:
Media Typeshttps://www.iana.org/assignments/media-types/media-types.xhtml
Gstreamer将Element Type类型用TypeNode来描述,每个类型都有一个对应的TypeNode,实际上是一个TypeNode类型的指针,保存的是创建出来的TypeNode的地址。有如下代码说明:
TypeNode *node = g_malloc0 (node_size);
Gtype type = (GType) node;
基本类型存放在static_fundamental_type_nodes数组中,子类型放在static_type_nodes_ht表中,二者在gtype.c的gobject_init函数中初始化。类型系统函数有如下几个常见的大写的缀,用来表示对Lock的需求,列举如下:
后缀 | 说明 |
_I | 函数对锁无需求,忽略锁 |
_U | 函数被调用前,不能加读写锁,因为函数本身可能会加锁。 |
_L | 函数被调用前,需要加写锁或者多个读锁。 |
_W | 函数被调用前,需要加写锁。 |
_Wm | 同_W,但这个写锁在函数里面可能会被释放,然后重新获取。 |
_WmREC | 同_Wm,但包含对mutex的互斥使用。 |
Gstreamer 的type常常用GType gst_ElementName_get_type(void)函数来获取,由定义在gtype.h中的G_DEFINE_TYPE_WITH_CODE这种类似的宏来定义。我们以pad为例,来看一下这个宏的定义情况。
G_DEFINE_TYPE_WITH_CODE(GstPad, gst_pad, GST_TYPE_OBJECT, G_ADD_PRIVATE(GstPad) _do_init);
展开后便是:
/* 初始化pad,理解成对象创建的构造函数初始化 */
static void gst_pad_init(GstPad *self);
/* 初始化pad的class,理解成类 */
static void gst_pad_class_init(GstPadClass *klass);
static GType gst_pad_get_type_once(void);
static gpointer gst_pad_parent_class = ((void *)0);
static gint GstPad_private_offset;
/* 理解成对象创建并且构造好后的赋初值 */
static void gst_pad_class_intern_init(gpointer klass)
{
gst_pad_parent_class = g_type_class_peek_parent(klass);
if (GstPad_private_offset != 0) {
g_type_class_adjust_private_offset(klass, &GstPad_private_offset);
}
gst_pad_class_init((GstPadClass *) klass);
}
static inline gpointer gst_pad_get_instance_private(GstPad *self)
{
return (((gpointer)((guint8 *)(self) + (glong)(GstPad_private_offset))));
}
GType gst_pad_get_type(void)
{
static gsize static_g_define_type_id = 0;
if ((g_once_init_enter((&static_g_define_type_id)))) {
GType g_define_type_id = gst_pad_get_type_once();
(g_once_init_leave((&static_g_define_type_id), (gsize)(g_define_type_id)));
}
return static_g_define_type_id;
}
static GType gst_pad_get_type_once(void)
{
/* 将类型注册到类型系统中 */
GType g_define_type_id = g_type_register_static_simple(
(gst_object_get_type()), g_intern_static_string("GstPad"), sizeof(GstPadClass),
(GClassInitFunc)(void (*)(void)) gst_pad_class_intern_init, sizeof(GstPad),
(GInstanceInitFunc)(void (*)(void)) gst_pad_init, (GTypeFlags) 0);
{
{
{
GstPad_private_offset =
g_type_add_instance_private(g_define_type_id, sizeof(GstPadPrivate));
}
_do_init;
}
}
return g_define_type_id;
};
GType
g_type_register_static_simple (GType parent_type,
const gchar *type_name,
guint class_size,
GClassInitFunc class_init,
guint instance_size,
GInstanceInitFunc instance_init,
GTypeFlags flags)
{
……
return g_type_register_static (parent_type, type_name, &info, flags);
}
GType
g_type_register_static (GType parent_type,
const gchar *type_name,
const GTypeInfo *info,
GTypeFlags flags)
{
TypeNode *pnode, *node;
GType type = 0;
……
/* 找到父类型的node */
pnode = lookup_type_node_I (parent_type);
G_WRITE_LOCK (&type_rw_lock);
/* 父节点引用加1 */
type_data_ref_Wm (pnode);
/* 检查父节点类型是否可以产生出type_name子类型 */
if (check_type_info_I (pnode, NODE_FUNDAMENTAL_TYPE (pnode), type_name, info))
{
/* 创建新的类型节点,g_malloc0出来的,n_supers表示父节点类型个数 */
node = type_node_new_W (pnode, type_name, NULL);
type_add_flags_W (node, flags);
type = NODE_TYPE (node);
/* 类型对应实例的配置函数初始化 */
type_data_make_W (node, info,
check_value_table_I (type_name, info->value_table) ? info->value_table : NULL);
}
G_WRITE_UNLOCK (&type_rw_lock);
return type;
}
说一下g_once_init_enter这个函数。这个函数定义在gthread.c中,采用了指针式的函数定义,原型是:
gboolean (g_once_init_enter) (volatile void *location);
用来分配GSList节点保存参数location,g_atomic_pointer_get (location) == 0成立时表示未进行GSList节点分配,location信息保存在g_once_init_list列表中,调用g_once_init_leave完成初始化之后,GSList节点会被释放掉。参数location代表的参数必须是静态参数,用0表示未初始化,初始化完成之后值非0,并且这个值在整个程序的生命周期中非0并且是唯一的。g_once_init_enter和g_once_init_leave配合使用确保临界区的代码只执行一次,未初始化完成之前,对函数的调用会被阻塞住。g_once_init_enter使用MemoryBarrier ()内存屏障函数来避免代码的乱序执行。
所以,Gtype的Type实际上就是TypeNode的地址,第一次调用GType gst_pad_get_type(void)函数之后,我们就创建了名为”GstPad”的类型节点TypeNode,完成初始化后将其添加到类型系统中。
Gstreamer类型的创建、初始化都是在gst.c的init_pos函数中,创建初始化并且将其引用加1。所有GLib types的共同点基本类型/非基本类型、能用class类来描述/不能用class类来描述,可实例化/不可实例化都可被Gvalue这个抽象封装描述。gstvalue.c文件中的_priv_gst_value_initialize函数则对这些内置类型及其转换函数进行了配置。
Typefind类型查找
Typefind就是查找数据流的类型,包括类型查找函数(类似ffmpeg的probe函数)和调用类型查找函数的函数(类似ffmpeg的av_probe_input_format函数)。因此,GStreamer需要一个能够自动使用类型查找系统的插件,以便动态地创建输入数据流的pipeline。类型查找函数位于gsttypefindfunctions.c插件内,这样做可以减少插件的数量,建议开发者按此规则添加新的类型,不要修改类型查找方式。