这是我参与11月更文挑战的第1天,活动详情查看:2021最后一次更文挑战
GStreamer Core
| 发送 | 接收 | 应答 | |
|---|---|---|---|
| message | element | bus | |
| event | Application, element | element | |
| query | application | element | application |
| buffer | element | element |
通信(Communication)
缓冲区Buffers
缓冲区Buffers是用来在Pipeline的元素Elements间传递流数据的,buffers通常是用从下游downStream一个Source节点流向一个Sink节点的
事件Events
事件Events通常在下游的元素Elements之间或者上下游的应用程序Application和元素Elements之间发送的。下游downstream的元素之间的事件Events可以用来做同步Synchronize使用
消息Messages
消息Message是由元素Elements发送到管道Pipeline的消息总线Message Bus上的,并在消息总线上被应用程序Application收集。消息可以从发布消息的元素Element的流线程上下文中同步截获消息,但通常由应用程序从应用程序的主线程异步处理。消息用于以线程安全的方式将错误、标记、状态更改、缓冲状态、重定向等信息从元素传输到应用程序。
查询Queries
查询允许应用程序从管道请求持续时间或当前播放位置等信息。查询总是以同步方式回答。元素还可以使用查询从它们的对等元素(例如文件大小或持续时间)请求信息。它们可以在管道中以两种方式使用,但上游查询更为常见。
Ogg Plyaer的GStreamer通信流
message/event/signal
Bus message —— 用于gstreamer和app之间交互的,比如当一个文件播放结束的时候,gstreamer会发一个EOS的message到GstBus上,如果app有去侦听(函数gst_bus_add_watch),那么在处理消息的callback函数中就可以收到这个消息。
event —— 用于gstreamer内部element之间(或pad之间)传递事件的,比如source element数据已经结束,会发出一个EOS event,顺着pipeline依次向downstream方向传递,elements得到通知,做一些cleanup工作,当所有sink element都收到并处理之后,gstreamer内部产生一条GstMessage,并post至GstBus,如果APP有监听,就能知道当前播放已经结束。
signal —— 属于GObject体系,用于app和GObject之间交互的一种机制。在gstreamer中,element本身也是gobject,所以通过signal,就可以将app和element联系起来。 当element发生了一些事情想让app知道时,就可以用signal的方式来通知app,比如动态创建了一个Pad。
每个pipeline默认有一个bus。所以应用程序不需要创建任何bus。应用程序唯一要做的就是在bus上设置一个消息处理程序。当 mainloop 运行时,bus会定期检查新消息,如果有消息可用时会调用回调函数(消息处理程序)。
Bus
- 每个Pipeline默认有一个bus
- 对bus的操作接口大部分都会上锁 GST_OBJECT_LOCK (bus) / GST_OBJECT_UNLOCK (bus)
- 向bus输入message
- gboolean gst_bus_post (GstBus * bus, GstMessage * message);
- 把message存入bus中。有两个主要的模式
- GST_BUS_PASS 模式只负责把消息存入队列,并更新write标记(监听read处知道可以读取)。
- GST_BUS_ASYNC 模式则在上一步基础上,获取消息的锁进行等待,直到消息被处理完后才返回(退出 gst_bus_post 函数)。
- gboolean gst_bus_post (GstBus * bus, GstMessage * message);
- 向bus取出message
- GstMessage * gst_bus_peek (GstBus * bus);
- 取出一个message,如果没有则返回NULL
- GstMessage * gst_bus_pop (GstBus * bus);
- GstMessage * gst_bus_pop_filtered (GstBus * bus, GstMessageType types);
- GstMessage * gst_bus_timed_pop (GstBus * bus, GstClockTime timeout);
- GstMessage * gst_bus_timed_pop_filtered (GstBus * bus, GstClockTime timeout, GstMessageType types);
- 等待/尝试/超时 获取一个特定类型的message
- GstMessage * gst_bus_poll (GstBus * bus, GstMessageType events, GstClockTime timeout);
- GstMessage * gst_bus_peek (GstBus * bus);
- 处理message
- void gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer user_data, GDestroyNotify notify);
- 在 post 时触发消息处理,不会加入轮询处理中。(与 post 处于同一个线程内处理)
- guint gst_bus_add_watch (GstBus * bus, GstBusFunc func, gpointer user_data);
- 把 func 关联到 signal_watch (新的 GSource) 中,默认 GMainContext 的轮询中。
- void gst_bus_add_signal_watch (GstBus * bus); // g_signal_connect() // info, error, eof, ....
- 使用 gst_bus_async_signal_func 调用 gst_bus_add_watch,在gst_bus_async_signal_func内部实现信号触发
- void gst_bus_set_sync_handler (GstBus * bus, GstBusSyncHandler func, gpointer user_data, GDestroyNotify notify);
- Bus 针对 message 的处理有2种方法:同步执行 sync_handler,异步执行(watch/signal)
- 总结:
- Bus 是线程安全(使用多种锁机制确保),内部使用队列进行消息接收和转发处理
- Message 适用于点对点的发送,一旦取出,必须执行(或销毁)
- Bus 提供同步消息处理,异步消息回调(或信号触发的机制)
struct GstBus {
GstObject object;
struct GstBusPrivate {
GstAtomicQueue *queue; // 队列(长度32)
GMutex queue_lock; // 队列锁
SyncHandler *sync_handler; // 同步消息处理,有新消息时第一时间调用(与 post 处在同一个context)
guint num_signal_watchers; // 与signal_watch互斥
guint num_sync_message_emitters; //
GSource *signal_watch; // 回调函数(异步)
gboolean enable_async;
GstPoll *poll; // 异步执行使用,内部使用 socketpair 创建一对无名互相连接的 socket
GPollFD pollfd; // 轮询句柄(与poll的句柄进行关联)
} *priv;
gpointer _gst_reserved[GST_PADDING];
};
struct GstMessage
{
GstMiniObject mini_object;
GstMessageType type;
guint64 timestamp;
GstObject *src;
guint32 seqnum;
GMutex lock;
GCond cond;
};
struct GstQuery
{
GstMiniObject mini_object;
GstQueryType type;
};
struct GstEvent {
GstMiniObject mini_object;
GstEventType type;
guint64 timestamp;
guint32 seqnum;
};
struct GstBuffer {
GstMiniObject mini_object;
struct GstBufferPool {
GstObject object;
gint flushing;
GstBufferPoolPrivate *priv;
gpointer _gst_reserved[GST_PADDING];
} *pool;
GstClockTime pts;
GstClockTime dts;
GstClockTime duration;
guint64 offset;
guint64 offset_end;
};
GMainLoop - GLib主事件循环
简介
GLib的主事件循环管理着任意数量不同类型的事件源(GSource),这个事件的来源如:文件描述符(文件、管道、套接字),超时。
新的事件源可以通过 g_source_attach() 添加。
为了让多组独立事件源能够在不同的线程中被处理,每个事件源都会关联一个GMainContext。一个线程只能运行一个GMainContext,但是在其他线程中能够对事件源进行添加和删除操作。
所有在 GMainContext 或内置 GSource 上运行的函数都是线程安全的。
每个事件源都被赋予了优先级。默认的优先级是G_PRIORITY_DEFAULT(0) 。值越小优先级越高,优先级高的事件源优先处理。
Idle函数在没有更高优先级的事件被处理的时候才会执行。
GMainLoop数据类型代表了一个主事件循环。通过g_main_loop_new() 来创建GMainLoop对象。在添加完初始事件源后执行g_main_loop_run() ,主循环将持续不断的检查每个事件源产生的新事件,然后分发它们,直到处理来自某个事件源的事件的时候触发了g_main_loop_quit() 调用退出主循环为止。
注意如果一个事件源被添加到一个GMainContext,那么它将被所有关联这个GMainContext的主线程检查和分发。
自定义事件源类型
通过继承GSource结构来创建一个新的事件源类型。继承产生的新事件源类型表示GSource结构作为新事件源类型的第一个元素然后其他元素紧跟其后。使用g_source_new() 函数来创建新的事件源类型实例,函数的参数就是新的事件源类型大小。GSourceFuncs决定新的事件源类型的行为。
新的事件源有两种基本方式与GMainContext交互。在它们GSourceFuncs中的准备函数能够设置睡眠事件,用来决定主循环下次检测它们的时间。也可以使用g_source_add_poll() 函数添加文件描述符到GMainContext进行检测。
自定义主循环迭代
执行g_main_context_iteration() 函数可以完成GMainContext的单次迭代。在一些情况下,我们可能想获取主循环更多的底层控制细节,我们可以调用g_main_context_iteration() 里的组件函数:g_main_context_prepare () 、g_main_context_query() 、g_main_context_check() 和g_main_context_dispatch() 。
Main Context 的状态
自定义事件源能操作的只有3+1个阶段(prepare、check、dispatch、以及MainContext销毁时finalize)
事件源内存管理
有两种的方式来管理传递给GSource回调函数用户数据的内存。这里的用户数据就是在调用g_timeout_add() 、g_timeout_add_full() 、g_idle_add() 传入的参数。这些数据通常被timeout或idle回调函数所拥有,比如一个构件或一个网路协议的实现。有些时候这些回调函数会在数据被销毁的后调用,因为使用了已经被释放的内存,这会导致出错。
- 第一种推荐的方法就是保存g_timeout_add() 、g_source_attach() 返回的事件源ID,然后在其维持的用户数据被释放后显示的将其从GMainContext移除。这样就能保证调用这些回调函数的时候,这些用户数据依然有效。(g_source_remove(source_id))
- 第二种就是保存这些回调函数中的用户数据对象的引用,然后在GDestroyNotify回调函数中释放它。这样就能确保数据对象在事件源最后一次调用然后被释放前一直有效。GDestroyNotify回调函数是GSource函数的一个变体的入参,它在事件源被释放时调用。
注意:第二种方法如果在事件源还没被调用前,主循环就结束的情况下,用户数据对象被维持状态是不确定的。