在Gst中创建动态管道(一)

本文介绍GStreamer框架下管道(pipeline)的构建过程,包括bin容器的使用、管道中各元素的链接方式及动态链接的设置等关键技术细节。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Gstreamer是一个灵活的构架,可以将各种不同的“元素”链接进一个管道(pipeline),允许在管道中自由地添加/删除现有的或新的“工具”以支持各种设备、编解码器(codec),甚至一些特殊的深加工设备。为实现这个目标,需要有一些复杂的assemle/negonatate机制用于链接元素。

bin和管道(pipeline

本章引自Gstreamer Application Development Manual
bin
是一个装载元素集合的容器。管道是特殊的bin类型,允许执行其中的所有子元素。由于bin本身是元素子类(subclass),通常可以像控制元素一样控制bin,从而简化应用程序。比如,可以通过改变bin本身的状态改变bin中所有元素的状态。bin还可以转发来自bin中的子元素的总线(bus)消息(例如错误消息,标签消息和EOS消息)。

管道是顶级bin。将它的状态设置为暂停(PAUSED)或播放(PLAYING)时,则数据流启动,媒体处理开始。启动后,管道将在一个单独的线程中运行,直到被停止或数据流结束。

http://gstreamer.freedesktop.org/data/doc/gstreamer/head/manual/html/images/simple-player.png 3-1:简单的ogg播放器的Gstreamer管道

示例代码

这里有一个来自gstreamer的示例文件:gst base seek example,该文件经LGPL许可。
有两种方法可以创建回放(playback)管道:

手动将元素链接到管道:

例如:make_wav_pipeline()make_vorbis_pipeline()make_mpeg_pipeline()。创建管道需要4个步骤,以make_vorbis_pipeline()为例:

1.     创建管道和元素

GstElement *pipeline, *audio_bin;
GstElement *src, *demux, *decoder, *convert, *audiosink;
GstPad *pad, *seekable;

pipeline = gst_pipeline_new ("app");

src = gst_element_factory_make_or_warn (SOURCE, "src");
demux = gst_element_factory_make_or_warn ("oggdemux", "demux");
decoder = gst_element_factory_make_or_warn ("vorbisdec", "decoder");
convert = gst_element_factory_make_or_warn ("audioconvert", "convert");
audiosink = gst_element_factory_make_or_warn (ASINK, "sink");
g_object_set (G_OBJECT (audiosink), "sync", TRUE, NULL);

g_object_set (G_OBJECT (src), "location", location, NULL);

audio_bin = gst_bin_new ("a_decoder_bin");

2.     将元素添加到管道

gst_bin_add (GST_BIN (pipeline), src);
gst_bin_add (GST_BIN (pipeline), demux);
gst_bin_add (GST_BIN (audio_bin), decoder);
gst_bin_add (GST_BIN (audio_bin), convert);
gst_bin_add (GST_BIN (audio_bin), audiosink);
gst_bin_add (GST_BIN (pipeline), audio_bin);

3.       链接元素和静态衰减器(static pad

gst_element_link (src, demux);
gst_element_link (decoder, convert);
gst_element_link (convert, audiosink);

4.       设置动态衰减器(dynamic pad)的动态链接(主要针对demuxer

pad = gst_element_get_pad (decoder, "sink");
gst_element_add_pad (audio_bin, gst_ghost_pad_new ("sink", pad));
gst_object_unref (pad);
setup_dynamic_link (demux, NULL, gst_element_get_pad (audio_bin, "sink"),
NULL); 


未完继续...

本文译自Moblin.org技术社区,  点击此处,查看原文


               更多内容,到“Moblin技术中国”专区



### 在 GStreamer 中动态添加或删除 Tee 的分支 #### 1. **Tee 元素的作用** `tee` 是 GStreamer 中的个核心元素,用于将输入数据复制到多个输出端口。它允许创建复杂的管道拓扑结构,其中同份数据可以被发送到不同的处理链路中[^1]。 当需要动态管理这些分支时(例如在运行过程中添加或删除某些分支),可以通过以下方式实现: --- #### 2. **动态添加分支** 要动态向 `tee` 添加新的分支,需遵循以下原则: - 创建个新的子管道。 - 将该子管道连接至 `tee` 的未链接 pad 上。 以下是具体的实现步骤及其代码示例: ##### a. 创建新分支 首先定义个完整的子管道逻辑,例如录制视频或将视频流转发给其他设备。 ```python import gi gi.require_version('Gst', '1.0') from gi.repository import Gst, GObject def create_new_branch(): """ 创建个新的分支 """ queue = Gst.ElementFactory.make("queue", "new_queue") video_convert = Gst.ElementFactory.make("videoconvert", "new_video_convert") autovideosink = Gst.ElementFactory.make("autovideosink", "new_autovideo_sink") branch_pipeline = Gst.Bin.new("new_branch_bin") branch_pipeline.add(queue, video_convert, autovideosink) # 链接元素 if not (queue.link(video_convert) and video_convert.link(autovideosink)): raise Exception("Failed to link elements in the new branch.") return branch_pipeline, queue ``` ##### b. 请求可用的请求垫并链接 通过调用 `request_pad()` 方法获取 `tee` 的下个可用请求垫,并将其与新建分支中的第个元素相连。 ```python def add_tee_branch(pipeline, tee): """ 动态添加个新的分支到 tee""" new_branch, first_elem_in_branch = create_new_branch() srcpad_template = tee.get_pad_template("src_%u") # 获取模板名称 new_srcpad = tee.request_pad(srcpad_template, None, None) # 请求新的 pad sinkpad = first_elem_in_branch.get_static_pad("sink") # 新分支的第个接收 pad if not new_srcpad.link(sinkpad).is_ok(): # 链接两者 raise Exception(f"Could not link newly created branch with tee.") pipeline.add(new_branch) # 将新分支加入主管道 new_branch.sync_state_with_parent() # 同步状态以激活新分支 ``` --- #### 3. **动态移除分支** 如果不再需要某个特定分支,则应按照以下顺序执行清理工作: - 断开 `tee` 和目标分支之间的连接; - 删除整个分支以及释放其占用资源。 下面是相应函数的设计思路及其实现代码: ##### a. 找到待移除的目标分支 定位即将销毁的部分,这通常依据某种唯标识符来进行检索。 ##### b. 解耦和销毁操作 断开端口关联后停止相关组件的工作流程,并从父容器里彻底剔除它们。 ```python def remove_tee_branch(tee, target_branch_first_element): """ 移除已存在的 tee 分支 """ # 定位对应的 source pad 并解除绑定 pads_to_release = [] for pad in tee.iterate_pads(): peer = pad.get_peer() if peer is not None and peer.get_direction() == Gst.PadDirection.SINK \ and peer.parent == target_branch_first_element: pads_to_release.append(pad) for release_pad in pads_to_release: release_pad.unlink(release_pad.get_peer()) # 取消链接 tee.release_request_pad(release_pad) # 归还 request pad # 设置分支进入 NULL 状态然后移除之 target_branch_first_element.set_state(Gst.State.NULL) parent_container = target_branch_first_element.get_parent() if isinstance(parent_container, Gst.Bin): parent_container.remove(target_branch_first_element.get_ancestor()) ``` --- #### 4. **注意事项** - 动态修改管道可能会引发同步问题,因此建议始终确保所有更改都在主线程安全环境下完成。 - 对于复杂的应用场景来说,考虑引入更高级别的抽象层来简化管理和维护成本可能是明智之举。 --- ### 示例完整代码片段 综合以上各部分内容形成段可实际测试的功能演示脚本如下所示: ```python if __name__ == "__main__": Gst.init(None) main_pipeline_desc = ''' videotestsrc name=source !\ tee name=t_splitter ''' main_pipeline = Gst.parse_launch(main_pipeline_desc) t_splitter = main_pipeline.get_by_name("t_splitter") try: loop = GObject.MainLoop() def on_key_press(keypress_event=None): global main_pipeline, t_splitter key = input("Enter command ('a' to add branch, 'r' to remove last added one): ") if key.strip().lower() == 'a': add_tee_branch(main_pipeline, t_splitter) elif key.strip().lower() == 'r': branches = list(filter(lambda e:e.name.startswith("new"), main_pipeline.children)) if len(branches)>0 : remove_tee_branch(t_splitter, branches[-1]) GLib.idle_add(on_key_press) main_pipeline.set_state(Gst.State.PLAYING) loop.run() except KeyboardInterrupt as kiex: print("\nStopping due to keyboard interrupt... ", end="") finally: main_pipeline.set_state(Gst.State.NULL) del main_pipeline ``` ---
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值