3. 开发环境搭建
好,我们正式开始进入 GStreamer 插件开发的核心地基——GObject。说实话,很多初学者一上来就被 GObject 的宏和命名规则搞懵了。我当年第一次看 GStreamer 源码时,心里直犯嘀咕:这玩意儿怎么这么多下划线?
但你别怕。GObject 说白了就是 C 语言里的一套面向对象框架。GStreamer 整个大厦都建在这上面。你写一个插件,本质上就是在 GObject 的体系里注册一个新类型,然后告诉框架:我这个元素能干什么。
3.1 GObject 类系统:C 语言里的“类”
C 语言没有 class 关键字,但 GObject 用结构体和函数指针模拟出了继承、封装和多态。核心就两个结构体:
- 实例结构体(Instance Struct):存放每个对象的私有数据。相当于 C++ 里的成员变量。
- 类结构体(Class Struct):存放虚函数表。相当于 C++ 里的虚函数表。
举个例子,你定义一个 GstMyFilter 元素,它的实例结构体里会放缓冲区指针、属性值;类结构体里放 chain 函数、sink_event 函数等虚函数。
核心原则:实例结构体是“数据”,类结构体是“行为”。两者通过类型系统绑定在一起。
3.2 类型注册:让 GObject 认识你
每个 GObject 子类都必须注册到类型系统。注册的核心是一个宏:G_DECLARE_FINAL_TYPE 或 G_DECLARE_DERIVABLE_TYPE。我个人习惯用 G_DECLARE_FINAL_TYPE,因为插件元素通常不需要再被继承。
注册流程分三步:
- 定义结构体:写实例结构体和类结构体。
- 实现
_get_type()函数:这个函数是类型系统的入口,里面调用g_type_register_static()。 - 实现
_init()和_class_init():初始化实例和类。
// 典型类型注册代码骨架
G_DEFINE_TYPE(GstMyFilter, gst_my_filter, GST_TYPE_ELEMENT)
static void gst_my_filter_init(GstMyFilter *filter) {
// 初始化实例成员
filter->sinkpad = NULL;
filter->srcpad = NULL;
}
static void gst_my_filter_class_init(GstMyFilterClass *klass) {
// 设置虚函数
GstElementClass *element_class = GST_ELEMENT_CLASS(klass);
gst_element_class_set_static_metadata(element_class,
"My Filter", "Filter/Effect",
"A simple filter element", "Your Name");
}
小技巧:G_DEFINE_TYPE 宏会自动生成 _get_type() 函数。你不需要手写。但如果你需要自定义父类初始化顺序,可以用 G_DEFINE_TYPE_WITH_CODE。
3.3 信号与属性:对象间的通信
GObject 提供了两种通信机制:
- 属性(Properties):用于读写对象的配置参数。比如视频元素的宽度、高度。
- 信号(Signals):用于事件通知。比如“缓冲区满了”、“流结束了”。
属性通过 g_object_class_install_properties() 注册。信号通过 g_signal_new() 注册。我在项目中遇到过一个问题:属性名和信号名不要用驼峰,要用连字符。比如 my-property 而不是 myProperty。否则 GStreamer 的管道描述语法会解析失败。
| 特性 | 属性(Properties) | 信号(Signals) |
|---|---|---|
| 用途 | 配置参数 | 事件通知 |
| 访问方式 | get/set 函数 | connect/emit |
| 典型例子 | bitrate, width | pad-added, eos |
| 注册函数 | g_object_class_install_properties | g_signal_new |
避坑指南:我曾经在信号处理函数里直接调用了 g_object_unref(),导致对象在信号传播过程中被销毁,程序崩溃。记住:信号处理期间,对象的引用计数由框架管理,不要手动释放。
3.4 GstObject 继承体系
GStreamer 的所有对象都继承自 GstObject。而 GstObject 本身继承自 GObject。整个继承链是这样的:
GObject
└── GstObject
├── GstElement
│ └── GstBin
│ └── GstPipeline
├── GstPad
├── GstBuffer
├── GstCaps
└── GstEvent
GstObject 在 GObject 基础上增加了:
- 名称管理:每个 GStreamer 对象都有一个名字,可以通过
gst_object_set_name()设置。 - 父子关系:对象可以形成树形结构,比如 bin 包含 element。
- 锁机制:
GST_OBJECT_LOCK()和GST_OBJECT_UNLOCK()用于线程安全。
你写插件时,你的元素类直接继承 GstElement。而 GstElement 又继承 GstObject。所以你天然拥有了名称、锁、父子关系这些能力。
关键点:GstObject 的引用计数是线程安全的。你不需要自己加锁来管理对象生命周期。但要注意,gst_object_ref() 和 gst_object_unref() 必须成对出现。
3.5 本章知识体系图
下面这张图展示了 GObject 类系统、类型注册、信号属性以及 GstObject 继承体系之间的关系。你可以把它当作一张地图,写插件时随时回来对照。
3.6 小结
这一章我们覆盖了三个核心概念:
- GObject 类系统:实例结构体存数据,类结构体存行为。
- 类型注册:用
G_DEFINE_TYPE宏让 GObject 认识你的新类型。 - 信号与属性:属性用于配置,信号用于通知。
- GstObject 继承体系:你的元素最终继承自 GObject,但中间经过了 GstObject 和 GstElement。