系列文章目录
- GStreamer 简明教程(一):环境搭建,运行 Basic Tutorial 1 Hello world!
- GStreamer 简明教程(二):基本概念介绍,Element 和 Pipeline
- GStreamer 简明教程(三):动态调整 Pipeline
- GStreamer 简明教程(四):Seek 以及获取文件时长
文章目录
前言
本章基于官方教程 Basic tutorial 6: Media formats and Pad Capabilities 进行一些说明和补充。本来想对 Basic tutorial 5: GUI toolkit integration 进行讨论的,但我的机器上安装 gtk 后运行程序总是崩溃,因此放弃。
在前面的章节中也有泛泛地提到过 Pad,例如 GStreamer 简明教程(二):基本概念介绍,Element 和 Pipeline和 GStreamer 简明教程(三):动态调整 Pipeline。接下来这一章,我们将对 Pad 这个在 GStreamer 中非常重要的概念进行详细阐述。
一、Pad 基本介绍
1.1 Pad
在 GStreamer 中,Pad 是连接不同元素之间的接口,允许它们相互通信和传递数据的概念。每个 GStreamer 元素都包含一个或多个 Pad,其中的数据流通过 Pad 传递。Pad 可以是输入 Pad,用来接收数据,也可以是输出 Pad,用于发送数据。
Pad 有不同的功能,可以用于传输不同类型的数据,如音频、视频或事件等。Pad 之间的连接是通过链接两个元素的 Pad 来实现的,它们会进行数据流的传递和处理。
在 GStreamer 中,Pad 扮演着非常重要的角色,它们定义了元素之间的连接和协作方式,使得不同元素能够协同工作完成复杂的多媒体处理任务。Pad 的概念使得 GStreamer 具有很强的灵活性和可扩展性,能够支持各种不同的数据流处理需求。
1.2 Pad 类型
在 GStreamer 中,Pad 的基本类型有两种:Sink Pad 和 Source Pad。这两种类型的 Pad 分别对应数据流的输入和输出端点。详细解释如下:
1. Sink Pad(接收 Pad)
- 作用:用于接收数据。Sink Pad 是元素接收数据的接口。
- 示例:一个解码器元素的 Sink Pad 会接收编码的音频或视频流。
- 常见使用场景:
- 解码器接收压缩的媒体流数据。
- 播放器接收解码后的音视频数据。
- 多路解复用器接收多个流的数据。
2. Source Pad(源 Pad)
- 作用:用于输出数据。Source Pad 是元素发送数据的接口。
- 示例:一个解码器元素的 Source Pad 会输出解码后的音频或视频流。
- 常见使用场景:
- 文件读取器(如
filesrc
)从文件中读取数据并通过 Source Pad 输出。 - 编码器将原始音视频数据编码后通过 Source Pad 输出。
- 多路复用器将多个流的数据组合后输出。
- 文件读取器(如
1.3 Pad 的模式
- 固定 Pad(Static Pad):在元素创建时就已经存在,并且它们的生命周期与元素同步。这些 Pad 静态地附着在元素上。
- 请求 Pad(Request Pad):这些 Pad 是动态创建的,只有在外部请求时才会生成。这种模式特别适用于需要动态添加或删除流的场景,如多路复用器或解复用器元素。
使用 gst-inspect 工具查看元素信息,通过 Pad Templates 中的信息判断 Pad 的模式,例如,以下是一个假设的元素输出的一部分:
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
video/x-raw
SINK template: 'sink_%u'
Availability: Sometimes
Capabilities:
video/x-raw
-
Static Pad:
例如上面示例中的SRC template
,其Availability
为Always
,表示这是一个 Static Pad。 -
Request Pad:
例如示例中的SINK template
,其Availability
为Sometimes
,表示这是一个 Request Pad(在特定需求时才会生成)。
这是一个具体例子,假设查看 audiotestsrc
元素的信息:
gst-inspect-1.0 audiotestsrc
部分输出可能类似如下:
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
audio/x-raw
format: S16LE
rate: [ 1, 2147483647 ]
channels: [ 1, 2147483647 ]
...
在这个例子中,src
是一个static pad,因为其 Availability
为 Always
。
二、Pad Templates
在GStreamer中,Pad(插口)是元素之间进行数据流动的接口,而Pad Template(插口模板)则是对这些接口进行预定义的蓝图。Pad Templates在GStreamer的流媒体处理中扮演着至关重要的角色,是描述元素间连接能力和数据流类型的基础。它们为元素间进行高效的连接协商和能力匹配提供了基础。
Pad Templates通过定义一个Pad可能具备的所有特性和能力(Capabilities),即其能够处理的数据格式和媒体类型。GStreamer使用Pad Templates在元素连接之前进行参数匹配,从而快速确定是否具备兼容性。
2.1 Pad Templates的重要性
-
描述性:
Pad Templates详细描述了Pad可能具备的所有能力,包括数据类型、格式、宽度、高度、采样率等信息。这些描述性信息对于流媒体处理中的连接协商是至关重要的。 -
效率提升:
在构建元素时,通过Pad Templates可以尽早判断元素之间的兼容性。若两个元素的Pad Template在能力上没有公共交集,则无需尝试进一步的连接操作。这大大提高了整个管道构建和数据流处理的效率。 -
动态Pad创建:
对于像多路复用器或解复用器这样的元素,可能需要根据实际情况动态创建Pad。Pad Templates为这些动态创建的Pad提供了标准,确保新创建的Pad符合预期且具备预定义的能力。
2.2 Pad Template的组成
每个Pad Template由以下部分组成:
-
Pad名称(Name Template):
描述Pad的名称模式,支持通配符。例如,sink_%u
表示一个动态创建的汇Pad,其名称后缀为数字。 -
Pad可用性(Pad Availability):
GST_PAD_ALWAYS
:Pad总是可用的,即Static Pad。GST_PAD_SOMETIMES
:Pad按需创建,即Request Pad。GST_PAD_REQUEST
:通过显式请求创建的Pad,即Request Pad。
-
Pad方向(Pad Direction):
GST_PAD_SRC
:源Pad,用于输出数据。GST_PAD_SINK
:汇Pad,用于输入数据。
-
Capabilities(能力):
描述Pad能够处理的数据格式和属性。这可能包括诸如媒体类型(音频、视频)、格式(如audio/x-raw
)、编码类型(如H.264)、分辨率、帧率、比特率等详细信息。
2.3 查看Pad Templates
通过GStreamer提供的工具gst-inspect-1.0
命令,可以查看某个元素的Pad Templates。例如:
gst-inspect-1.0 videotestsrc
示例输出:
Pad Templates:
SRC template: 'src'
Availability: Always
Capabilities:
video/x-raw
format: RGB
width: [ 1, 2147483647 ]
height: [ 1, 2147483647 ]
2.4 Pad Templates在协商过程中的作用
Pad Templates是连接协商过程的第一步。元素在建立连接之前,会通过它们的Pad Templates进行初步匹配。如果两个Pad的模板在定义的能力上存在交集,则进行下一步的协商。实际建立连接后,两个Pad的实际能力可能根据协商结果进一步细化,直到最终确定。
三、Pad Capabilities
在GStreamer中,Pads(插口)是元素间数据流动的接口,而Pad Capabilities(插口能力)是这些接口所能处理的数据类型和属性的详细描述。Pad Capabilities起到定义和描述数据格式的关键作用,为元素间的连接协商奠定基础。
Pad Capabilities是一个结构化的数据描述,它定义了一个Pad能够接受和输出的数据格式、媒体类型及其可能的属性范围。这些能力定义在创建Pad Templates(插口模板)时就已明确,用于元素之间的互操作性和连接匹配。
3.1 组成部分
Pad Capabilities由多个字段组成,主要包括以下内容:
-
媒体类型(Media Type):
描述数据的基本类型,例如音频(audio)、视频(video)、图片(image)等。每种媒体类型还有相应的次级类型,例如audio/x-raw
表示原始音频数据。 -
格式(Format):
指定具体的数据格式,例如音频的位深度、编码格式,视频的像素格式等。 -
属性(Properties):
用于进一步细化媒体数据的描述,包括但不限于以下部分:- 音频属性:
- 比特率(bitrate)
- 采样率(sample rate)
- 声道数(channels)
- 视频属性:
- 分辨率(resolution):宽度(width)和高度(height)
- 帧率(framerate)
- 色彩格式(color format)
- 音频属性:
-
范围和集合(Ranges and Sets):
Capabilities中的一些属性可以是范围或集合,以表示该能力的变化范围或多个可能的值。例如:- 范围(Range):
[ 1, 2147483647 ]
表示从1到2147483647之间的所有值均有效。 - 集合(Set):
{ I420, NV12, NV21 }
表示可以是I420、NV12或NV21中的任意一种。
- 范围(Range):
3.2 定义和使用
Capabilities通常在Pad Template中定义,用来描述一个Pad可能的全部能力。GStreamer使用这些定义在元素连接前进行初步匹配。如果两个Pad的模板在能力上没有公共交集,那么进一步连接会被跳过,从而提高处理效率。
3.3 Capabilities示例
以下是一些Pad Capabilities的示例:
-
音频Capabilties:
audio/x-raw format: S16LE rate: [ 1, 2147483647 ] channels: [ 1, 2 ]
- 媒体类型:
audio/x-raw
,表示原始音频数据。 - 格式:
S16LE
,16位有符号小端格式。 - 采样率:
rate: [ 1, 2147483647 ]
,从1到2147483647的范围。 - 声道数:
channels: [ 1, 2 ]
,表示1到2个声道。
- 媒体类型:
-
视频Capabilities:
video/x-raw width: [ 1, 2147483647 ] height: [ 1, 2147483647 ] framerate: [ 0/1, 2147483647/1 ] format: { I420, NV12, NV21, YV12, YUY2, Y42B, Y444, YUV9, YVU9, Y41B, Y800, Y8, GREY, Y16, UYVY, YVYU, IYU1, v308, AYUV, A420 }
- 媒体类型:
video/x-raw
,表示原始视频数据。 - 分辨率:
width: [ 1, 2147483647 ]
和height: [ 1, 2147483647 ]
,表示支持从1到2147483647范围的宽度和高度。 - 帧率:
framerate: [ 0/1, 2147483647/1 ]
,表示支持从0/1到2147483647/1之间的帧率。 - 格式:
format: { I420, NV12, NV21, ... }
,表示可以是I420、NV12、NV21等多种图像格式。
- 媒体类型:
3.4 连接协商(Negotiation)
在管道构建过程中,Pad Capabilities是连接协商的关键元素。元素通过各自的Pad Template中预定义的Capabilities进行匹配。如果两个元素的Pad在其Capabilities上具有公共交集,则可以建立连接并进一步协商实际的数据传输格式。
协商过程通常包括以下步骤:
-
初步匹配(Initial Match):
元素根据Pad Template的Capabilities进行初步匹配。如果找不到公共交集,则连接失败。 -
详细协商(Detailed Negotiation):
一旦初步匹配成功,元素进一步协商具体的格式、帧率、分辨率等详细参数,直到达成一致或协商失败。 -
最终确定(Final Fixation):
确定最终的传输格式和参数后,Pad的Capabilities被固定,后续数据流按照协商结果进行传输。
四、Basic tutorial 6: Media formats and Pad Capabilities
接下来对 Basic tutorial 6: Media formats and Pad Capabilities 代码部分进行说明,代码太长就不贴了。这部分代码想说明几个内容:
- 如何获取元素的 Pad Templates 信息
- 如何获取元素的 Pad Capabilities 信息
- 不同状态下元素的 Pad Capabilities 的切换
4.1 打印 Pad Templates
static void print_pad_templates_information (GstElementFactory * factory) {
const GList *pads;
GstStaticPadTemplate *padtemplate;
g_print ("Pad Templates for %s:\n", gst_element_factory_get_longname (factory));
if (!gst_element_factory_get_num_pad_templates (factory)) {
g_print (" none\n");
return;
}
pads = gst_element_factory_get_static_pad_templates (factory);
while (pads) {
padtemplate = pads->data;
pads = g_list_next (pads);
if (padtemplate->direction == GST_PAD_SRC)
g_print (" SRC template: '%s'\n", padtemplate->name_template);
else if (padtemplate->direction == GST_PAD_SINK)
g_print (" SINK template: '%s'\n", padtemplate->name_template);
else
g_print (" UNKNOWN!!! template: '%s'\n", padtemplate->name_template);
if (padtemplate->presence == GST_PAD_ALWAYS)
g_print (" Availability: Always\n");
else if (padtemplate->presence == GST_PAD_SOMETIMES)
g_print (" Availability: Sometimes\n");
else if (padtemplate->presence == GST_PAD_REQUEST)
g_print (" Availability: On request\n");
else
g_print (" Availability: UNKNOWN!!!\n");
if (padtemplate->static_caps.string) {
GstCaps *caps;
g_print (" Capabilities:\n");
caps = gst_static_caps_get (&padtemplate->static_caps);
print_caps (caps, " ");
gst_caps_unref (caps);
}
g_print ("\n");
}
}
这段代码是一个 GStreamer 相关的函数,用于打印一个 GstElementFactory(元素工厂)的 pad 模板信息。以下是代码的详细解释:
-
函数接受一个 GstElementFactory 指针作为参数。
-
首先打印元素工厂的长名称。
-
检查元素是否有 pad 模板,如果没有,打印 “none” 并返回。
-
获取元素工厂的静态 pad 模板列表。
-
遍历 pad 模板列表,对每个模板执行以下操作:
- 根据 pad 的方向(SRC、SINK 或未知)打印相应的信息。
- 打印 pad 的可用性(Always、Sometimes、On request 或未知)。
- 如果 pad 模板有静态能力(caps),则打印能力信息。
-
打印能力信息时,使用
print_caps
函数(未在代码中给出)来格式化输出。
输入如下,整体和 gst-inspect 输出类似
Pad Templates for Audio test source:
SRC template: 'src'
Availability: Always
Capabilities:
audio/x-raw
format: { (string)S16LE, (string)S16BE, (string)U16LE, (string)U16BE, (string)S24_32LE, (string)S24_32BE, (string)U24_32LE, (string)U24_32BE, (string)S32LE, (string)S32BE, (string)U32LE, (string)U32BE, (string)S24LE, (string)S24BE, (string)U24LE, (string)U24BE, (string)S20LE, (string)S20BE, (string)U20LE, (string)U20BE, (string)S18LE, (string)S18BE, (string)U18LE, (string)U18BE, (string)F32LE, (string)F32BE, (string)F64LE, (string)F64BE, (string)S8, (string)U8 }
layout: { (string)interleaved, (string)non-interleaved }
rate: [ 1, 2147483647 ]
channels: [ 1, 2147483647 ]
Pad Templates for Auto audio sink:
SINK template: 'sink'
Availability: Always
Capabilities:
ANY
4.2 打印 Pad Capabilities
static void print_pad_capabilities (GstElement *element, gchar *pad_name) {
GstPad *pad = NULL;
GstCaps *caps = NULL;
/* Retrieve pad */
pad = gst_element_get_static_pad (element, pad_name);
if (!pad) {
g_printerr ("Could not retrieve pad '%s'\n", pad_name);
return;
}
/* Retrieve negotiated caps (or acceptable caps if negotiation is not finished yet) */
caps = gst_pad_get_current_caps (pad);
if (!caps)
caps = gst_pad_query_caps (pad, NULL);
/* Print and free */
g_print ("Caps for the %s pad:\n", pad_name);
print_caps (caps, " ");
gst_caps_unref (caps);
gst_object_unref (pad);
}
static gboolean
print_field (GQuark field, const GValue * value, gpointer pfx)
{
gchar *str = gst_value_serialize (value);
g_print ("%s %15s: %s\n", (gchar *) pfx, g_quark_to_string (field), str);
g_free (str);
return TRUE;
}
static void
print_caps (const GstCaps * caps, const gchar * pfx)
{
guint i;
g_return_if_fail (caps != NULL);
if (gst_caps_is_any (caps)) {
g_print ("%sANY\n", pfx);
return;
}
if (gst_caps_is_empty (caps)) {
g_print ("%sEMPTY\n", pfx);
return;
}
for (i = 0; i < gst_caps_get_size (caps); i++) {
GstStructure *structure = gst_caps_get_structure (caps, i);
g_print ("%s%s\n", pfx, gst_structure_get_name (structure));
gst_structure_foreach (structure, print_field, (gpointer) pfx);
}
}
上面三个函数用于打印 GStreamer 元素 pad 的能力(capabilities)信息。让我逐一解释:
-
print_pad_capabilities
函数:- 接受一个 GstElement 和 pad 名称作为参数。
- 获取指定的 pad。
- 尝试获取当前协商好的 caps,如果没有,则查询可接受的 caps。
- 调用
print_caps
函数打印 caps 信息。 - 最后释放资源。
-
print_field
函数:- 这是一个回调函数,用于打印单个字段的信息。
- 将字段值序列化为字符串并打印。
- 返回 TRUE 以继续遍历。
-
print_caps
函数:- 打印 GstCaps 的详细信息。
- 如果 caps 是 ANY 或 EMPTY,直接打印对应信息。
- 否则,遍历 caps 中的每个结构(structure)。
- 对每个结构,打印其名称,然后使用
print_field
函数打印每个字段的详细信息。
这些函数共同工作来提供 GStreamer 元素 pad 的详细能力信息:
print_pad_capabilities
获取特定 pad 的 caps。print_caps
负责整体 caps 的打印逻辑。print_field
处理单个字段的打印。
4.3 不同状态下 Pad Capabilities 的变化
示例代码中,分别在不同转态下打印了 autoaudiosink 元素的 pad caps 变换,如下:
In NULL state:
Caps for the sink pad:
ANY
Pipeline state changed from NULL to READY:
Caps for the sink pad:
audio/x-raw
format: F32LE
layout: interleaved
rate: 48000
channels: 2
channel-mask: 0x0000000000000003
audio/x-raw
format: { (string)F64LE, (string)F64BE, (string)F32LE, (string)F32BE, (string)S32LE, (string)S32BE, (string)U32LE, (string)U32BE, (string)S24_32LE, (string)S24_32BE, (string)U24_32LE, (string)U24_32BE, (string)S24LE, (string)S24BE, (string)U24LE, (string)U24BE, (string)S20LE, (string)S20BE, (string)U20LE, (string)U20BE, (string)S18LE, (string)S18BE, (string)U18LE, (string)U18BE, (string)S16LE, (string)S16BE, (string)U16LE, (string)U16BE, (string)S8, (string)U8 }
layout: interleaved
rate: [ 1, 2147483647 ]
channels: 2
channel-mask: 0x0000000000000003
audio/x-raw
format: { (string)F64LE, (string)F64BE, (string)F32LE, (string)F32BE, (string)S32LE, (string)S32BE, (string)U32LE, (string)U32BE, (string)S24_32LE, (string)S24_32BE, (string)U24_32LE, (string)U24_32BE, (string)S24LE, (string)S24BE, (string)U24LE, (string)U24BE, (string)S20LE, (string)S20BE, (string)U20LE, (string)U20BE, (string)S18LE, (string)S18BE, (string)U18LE, (string)U18BE, (string)S16LE, (string)S16BE, (string)U16LE, (string)U16BE, (string)S8, (string)U8 }
layout: interleaved
rate: [ 1, 2147483647 ]
channels: 1
Pipeline state changed from READY to PAUSED:
Caps for the sink pad:
audio/x-raw
format: F32LE
layout: interleaved
rate: 48000
channels: 2
channel-mask: 0x0000000000000003
Pipeline state changed from PAUSED to PLAYING:
Caps for the sink pad:
audio/x-raw
format: F32LE
layout: interleaved
rate: 48000
channels: 2
channel-mask: 0x0000000000000003
这个输出展示了 autoaudiosink 元素在不同状态下 sink pad 的能力(capabilities)变化。让我们逐一解释:
-
NULL 状态:
- Caps 显示为 ANY,这意味着在 NULL 状态下,元素还没有进行任何初始化或配置。它可以接受任何类型的音频输入。
-
READY 状态:
- 在这个状态下,autoaudiosink 已经初始化,但还没有分配资源或开始处理数据。
- 显示了三种不同的 caps 配置:
a. 第一个是一个具体的配置,可能是基于系统默认音频设置。
b. 第二个和第三个是更广泛的配置,显示了元素可以支持的各种音频格式、采样率和通道数。 - 这表明 autoaudiosink 在 READY 状态下已经查询了系统音频capabilities,但还没有固定到特定的配置。
-
PAUSED 状态:
- Caps 变得更加具体,固定为一种特定的格式(F32LE,48000Hz,2通道)。
- 这表明在 PAUSED 状态下,autoaudiosink 已经与上游元素协商并选择了一个特定的音频格式。
-
PLAYING 状态:
- Caps 保持与 PAUSED 状态相同。
- 这表明 PLAYING 状态没有引起进一步的 caps 变化,元素已经准备好以协商好的格式处理音频数据。
总结:
- NULL 到 READY:元素初始化并收集可能的音频配置。
- READY 到 PAUSED:元素与管道中的其他元素协商,选择特定的音频格式。
- PAUSED 到 PLAYING:保持协商好的格式,开始处理音频数据。
这个过程展示了 GStreamer 元素如何逐步从一个通用状态转变为特定配置,以适应整个管道的需求。它也说明了为什么在构建 GStreamer 管道时,将元素推进到 PAUSED 状态对于正确配置很重要。