引擎盖下的管道

2021-06-23 22:04:24

Pipewire项目正在慢慢地让Porthalas它成熟。它的文档仍然相对稀少,但逐渐涌现。然而,让outsideethe项目中的人们试图掌握并以自己的话说,重申想法,从他们自己的角度来看,这总是一个好主意。

在职前,在Unix上的通用音频堆栈中,有一个部分提到了pipewire。不幸的是,因为当时我没有发现足够的文档无法围绕一些概念包裹我的头,我想我没有向这个项目那么脱节,并且可能会困惑一些部分。在这篇文章中,我会尝试以最简单的方式解释PipeWire,使其可以访问那些想要在这个CoolNew项目之后开始的人,但不知道在哪里开始。为更多人加入并遵循TheCurrent开发时,这尤其重要,以便打开更多人,并以快速的节奏发生。

免责声明:我想前言,通过说我不是项目中的开发人员,只有一个兴趣的互联网旅行者。 imight仍然犯了错误,不涵盖一切,所以可以确保托成评论或电子邮件,以便我可以纠正或添加信息。 PS:如果您喜欢深度潜水和类似的讨论,那么检查尼克斯社区,它充满了LoveTo的人。 PPS:我使用了一个类似的名字作为神话般的脉冲血清作者,但我认为我认为我在作者对Pulseaudio做出了很多细节。

PipeWire是一种媒体处理图,这可能不会响铃,所以别是learme rephrase。 PipeWire是一个守护程序,提供相当于spellpipes,但是对于媒体:音频和视频。

此图中的生命是可以代表多个事物,从诸如耳机或网络摄像头的真实设备中代表多个东西的节点,以虚拟滤波器。这些节点具有端口,这些端口可以链接在一起,主题在NextNode的接收器的方向上从第一节点的源流程。每个节点中发生的事情都取决于它们以及它们提供的interfacesor功能。

实际上,节点,链接,端口和其他人都是延伸基本类型的不同Objects,并在此图中居住。这些对象不一定必须是与媒体相关的,他们可以做很多其他事情。我们会看到他们使用特殊类型的系统,插件系统和编组格式/存储。

所有人都说,皮舰是一个图表,“它的机制而不是政策”。这意味着要创建和交互,我们需要软件的另一个图形。标准方式是依靠PipeWire Calla会话管理器或策略管理器。这种软件的作用是在图表中进行创建的,具体取决于环境,例如当插入设备时,或者需要恢复卷策略,或者在允许客户端允许客户端进入访问设备。

目前,此类会话管理器有两种实现:默认的一个名为Pipewire-Media-Session的一个,以及一项工作,但非常有前途和有趣的一个,称为WirePlumber。然而,您可以选择在外部工具上建立自己的会话管理工作流程来管理PipeWire图中的内容。

让我们剪短这么短,看看如何运行Pipewire。从包管理器获取它,并在终端中执行以下操作。

在某些情况下,正如我们从PipeWire配置中看到的那样,您可能会发现执行第二个命令,因为Pipewire可以设置为utomical上启动PipeWire-Media会话。事先尝试一个ps只是为了确定。

尽管如此,如果没有Pipewire的应用程序,那么上面就不会在任何地方接受你的地方 - 目前只有少数人。此外,我们看到的,皮舰扭音和工具仍然缺乏。这就是为什么,Pipewire提供三个兼容性层:用Alsathrough通过PipeWire-Pulse Server使用Pulseaudio,以及通过PW-jack命令的插孔。

要确保您获得了ALSA兼容性,请检查/etc/alsa/conf.d/50-pipewire.conf,它应该定义PCMFor Pipewire,通常在安装PipeWire-Alsapackage时创建。然后检查是否已通过alsactl dump-cfg或aconfdump或验证pcm.default条目,检查是否已成为默认的PCM Bydumping Alsa配置。但是,如果您仍然想要覆盖Pulseaudio垫片,则在这种情况下,它将是型脉冲。

对于Pulseaudio层,它允许使用Pulseaudio工具用水,安装PipeWire脉冲并启动它。您的分发包装甚至通过init / service manager自动启动它。

最后,对于千斤顶,在任何杰克相关命令之前安装Pipewire-Jack包并发出PW-Jack,他们将自动使用PipeWire。例如:

此外,对于视频功能,您应该安装桌面门户,这是实现XDG-Desktop-PortalsPecifications的DBus服务,其作业是检查是否允许客户端请求访问视频。有许多这样的软件:

如果您正在运行Wayland CompoSitoras,您应该熟悉这些,这是在Wayland(网络摄像头,屏幕清醒,屏幕截图)上访问视频的唯一方法。

在这里,Pipewire正在运行!然而,除此之外,诸如如何配置pipewire和会话管理器,如何编写依赖onpipewire的软件,依赖于它的思维,如何操纵graphand,以及许多其他例子。

理解Pipewire背后的想法的最佳方式是采取一款Lookat GStreamer,它激发了由同一个领导开发人员(一个漂亮而欢迎的人)维持的。尽管笔者想要与jackinstead进行比较。你可以看看杰克并从中提取你想要的概念,但我们会在本文中寻找Gstreamer。

GStreamer围绕从对象加入的流水线的概念播放在“bin”,它的图形版本。像管道一样,媒体流动到另一个端子。在GStreamer的世界中,而不是那里的节点,而不是端口有附加到GStelements的PAD,并且链接是焊盘之间的连接。

相似的变得更深,GStreamer依赖于Gobject,其中,如果您没有意识到,是一个C框架/编程模型,用于制作开发者。它允许它做多个事情,例如登记由管道中的gstelementspresent触发的伪装事件(称为信号)。 Gobject还具有主循环管理,一种类型系统,内省机制和其他好东西。

GStreamer提供不同类型的GsteLements作为插件培布的“工厂”。实际上,这些是Gobjects扩展了Common Gstelement类型以提供有用的功能。 GStreamer文档中有它们可以使用它们。例如aasink。有些是视频,有些用于音频,有些用于记录,有些用于转换与康复格式和协商等。

这些元素也可以通过GST-Inspect-1.0工具在命令行上进行内部。这使得它易于易于编程。

同样,PipeWire具有扩展基本节点ElementType的插件。但是,它不依赖于Gobject / GstElement,而是在其OwnSimpler插件系统上,适当命名为SPA(简单插件API)。 ITALSO具有工厂,循环管理系统,异步事件,以及名为POD(普通旧数据)的消息传递格式。将PipeWire视为一个简单的GStreamer作为守护程序运行,具有完全控制的循环,并且依赖于会话管理器基于出现的流和设备自动创建图形/管道。

然而,Pipewire仍然缺少文档和内省工具ASexcellent作为GStreamer。插件尚未记录。我不得不说,GStreamer是一个真正做到好的项目,我希望Pipewire威尔逊是一样的。

GStreamer仍然与管道相关,因为GStreamer可以随着它与它一起集成。 “GStreamer旨在成为Multimedia的瑞士军刀”。 PipeWire的新插件可在名称下提供:

学习Gstreamer是更好地了解Pipewire的好方法,至少是对我来说。

Gobject是Glib对象系统,是众所周知和战斗的,但由于其沉重,这不是Pipewire使用的。 Pipewire Relieson Spa和Pod。那么什么是SPA(简单的插件API)和POD(普通旧数据)?

POD,普通的旧数据(不与Perl的格式文档混淆)是用于编组/解马语的迭代数据容器格式,序列化/不定次化,存储,AndTransfer。这是通常的平坦被动数据结构。

将其视为另一种格式,类似于XML,JSON,ASN.1,PROTOBUF等。它激发了D-Busvariantand LV2原子等格式。

实际上,它是LTV(长度值)格式,从而使用occtet计数帧,其中长度和类型是固定的32bits值,帧总是8字节对齐。因此,填充通常添加了不对齐的迹象。 (NB:框架是指价值的概念,如何分隔。)类型系统,32位T引用的是什么,称为SPA类型系统,具有复合/容器和基本/原始类型。这些范围从包含阵列,结构,对象,序列,指针,文件描述符,选择,如bool,int,string等的基元。这种格式的优势在于它是“AS-IS”格式,它念珠地在网络上传输,从内存中读取,存储在墓穴或磁盘上而无需额外的编组。

NB:如果您想了解更多关于协议/格式设计的信息,通常会转到RFC 3117。

POD库未设计专门用于PipeWire,它可以在任何其他项目中使用,但问题仍然是为什么为什么使用此格式而不是另一个格式。库是一个小型标头C库漏洞依赖项,这使得它可以使用它来测试。我发表了一个关于itustage的一个例子,在正式特性上,但让我们仍然越过它的一般方面。

Pod的最佳文档是咨询标题本身,ASA很多帮助者都没有记录在其他任何地方。您通常可以在/ usr / include / spa / pod /或/ usr / compress / spa-<版本> / pod /(只需确定它是最新版本)。它与SPA捆绑在一起,因为它依赖于/usr/include/spa/utils/type.h和defs.h中的类型ID系统。

POD结构STRUCH SPA_POD在/spa/pod/pod.h中定义为基本和容器值。构建它们在/spa/pod/builder中发现它们的构建器,而解析器在/spa/pod/parser.h中,可以使用/spa/pod/iter.h,/ spa中的帮助程序完成操作/pod/filter.h等人。

实际上,要创建一个豆荚,我们初始化内存的任何部分,都是Inthe堆或堆栈,并使用Builder帮助程序初始化它。然后我们依靠框架来设置容器对象的开始并派生,基本上设置对象的最终大小(如我们所说的LTV)。框架充当我们选择的存储段的值的一种推动和流行。

这是它的样子,您可以直接查阅MyExample,官方文档,或标题以了解更多信息。编译应该像CC Pod-test.c -o pod-test一样简单。

我们定义了任何类型的存储,在这里,堆栈上的256B,并告诉PodBuilder我们将使用它来存储POD。

然后,我们可以为容器对象定义一个帧,这里是一个简单的结构,但它可能是对象(键/值,称为属性,也拥有自己的ID)或其他复杂类型。

最后,我们关闭框架可以说我们使用struct完成,而iTreturns可以将稍后投射的结构spa_pod基本类型进行适当的类型。

长度= 00000020 = 32tepe = 0000000e = 14 = spa_type_structvalue = length = 00000004 = 4 type = 00000004 = spa_type_int值= 00000005 = 5 _padding = 00000000(始终对齐8个字节)长度= 00000004 = 4 type = 00000006 = spa_type_float值= 40490e56 = 3.1415 _padding = 00000000(始终对齐8个字节)

这些类型可以在/spa/utils/type.h和其他文件中找到。我们调送对对齐的填充,这是由建设者自动添加的。

一旦我们创建了特定类型的POD,我们就可以处理它备用助手。在erter.h标题中,我们可以找到循环循环spa_pod_struct或方法来验证pod是否确实是结构的方法。

struct spa_pod *条目; spa_pod_struct_foreach(example_struct,条目){printf("字段类型:%d \ n",输入 - >类型); //两种获取值,铸造或使用spa_pod_get_ .. if(spa_pod_is_int(条目)){int32_t inival; spa_pod_get_int(条目,& over); printf("找到int,pod_int:%d \ n",inive); }如果(spa_pod_is_float(条目)){struct spa_pod_float * pod_float =(struct spa_pod_float *)条目; printf("找到float,pod_float:%f \ n",pod_float->价值); }}

正如您所知,STRUCT SPA_POD是通用的,只包含POD,即大小和类型的第两个32bits值。因此,我们必须致授予其类型以找出它所实际的内容,然后在Iter.h中发现它或使用诸如spa_pod_get_ *之类的帮助者

而不是这样做,我们可以依赖spa_pod_parser和其他帮助程序来执行原始数据的验证和检查(例如,在iter.h spa_pod_from_data中看到)。

然而,当您只想浏览其格式时,手动检查值会很烦人。这就是为什么调试功能存在于/spa/debug/pod.h中。 spa_debug_pod_value尤其如上使用了pod中的内容。我们也可以使用/ spa/utils/json.hand / spa/utils/ansi.h将结构打印为JSON,就像这里显示一样,但没有简单的功能才能做到这一点。

豆荚比这更多地有很多,它是一个整个对象格式,butlet的停止和移动到水疗中心。

虽然POD是关于数据表示的,但SPA是关于功能的。 SPA,SPA,SIMPLE插件API是一个标头和Nodependencies框架,它能够加载库的特定格式,枚举工厂,创建它们以及它们提供的Interpace,它们在运行时均可提供。

像豆荚一样,它不一定与管道绑定,而是可以使用,但是使用Pipewire使用Spa获得自己的插件。

SPA中的工厂创建的对象具有特定的格式,通常在内部依赖于POD,并且SPA工厂界面界面我们知道与它们相关的功能。

实际上,使用SPA的插件采用动态Loadablibraries(.so)的形式,通常在/ usr / lib / spa /,或env spa_plugin_path下找到,并使用dlopen(3)在飞行上打开。 SPA Libs至少有一个公共符号spa_handle_factory_enum,它定义为/ spa/support/plugin.h,其如下加载。

我还写了一个simpleexampleto show spa用法,但让我们迅速审查该机制哮喘很重要,可以掌握PipeWire配置。编译也应该像CC Test-spa.c-ll -o test-spa一样简单。

插件由工厂列表组成,在每个工厂中我们都有可用的接口列表。这个想法是,工厂是一种围绕特定类型的结构,从其创建到不同的互动。接口是关于任何那个那个捆绑在一起的东西。

工厂的名字和它们中的接口有namestoo,它们是在/spa/utils/names.h中定义的,并且在form spa_type_interface_ *中在插件标题本身中遇到的接口名称。工厂还提供了版本,作者,描述等的附加信息。

这就是我们需要知道加载后使用库。然而,我们必须咨询标题文件和库位置/ usr / lib / spa-<版本>要知道目前是否有哪些工厂,并且可以使用它们。在该库目录中,文件夹显示显示插件类别,其中大多数与Pipewire有关。如果我们看看支持/ libspa-support.so,我们会看到它relatesto我们得到的支持标题。我们可以在spa_type_Interface_to找到包含插件的标题以确定。让我们看看/ log.h作为一个例子。

出厂名称是spa_name_support_log,唯一的接口deviceis spa_type_interface_log。在内部,我们看到所有插件Usingspa定义了两件事:包含一个包含与该接口相关的方法的STRACH SPA_INTERFACE ANDANITHER STRUCT以及版本,另外它可以定义事件和回调。如果您可以在/spa/utils/hook.h中定义了您的strucious stuct spa_interface。但是,内部工作并不重要,重要的是,我们介绍了一个函数在一个接口中,这是一个与插件一起做的界面。

一系列命令行工具以SPA开头 - 与插件一起输入。特别是一个调用spa-inspect可以用来转储,用于转储.so文件中存在的工厂和接口。然而,它不是很坚固,没有授予关于我们可以用插件做的很多信息。

因此,在加载.so和获取enum_func函数后,我们可以循环源,找到我们感兴趣的名称,然后循环遍布其接口,看看它是否有我们想要的一个,而GetAn的工厂实例则循环用它。

uint32_t i; const struct spa_handle_factory * factory = null; for(i = 0 ;;){if(enum_func(&工厂,& i)< = 0)突破; Printf("工厂名称:%s,版本:%d \ n",factory->名称,factory->版本); if(strcmp(factory-> name,spa_name_support_log)== 0){const struct spa_interface_info * info = null; uint32_t index = 0; //在位置0 int interface_available = spa_handle_factory_enum_interface_info(工厂,&信息,&索引); if(strcmp(info-> type,spa_type_interface_log)== 0){//分配一个句柄(结构)指向//到此工厂' s接口size_t size = spa_handle_factory_get_size(factory,null); struct spa_handle * handle = calloc(1,尺寸); spa_handle_factory_init(出厂,句柄,null,// info null,//支持0 // n_support); //按出厂手柄VOID * iFace的名称获取接口; int interface_exists = spa_handle_get_interface(句柄,spa_type_interface_log,& iface); //最后通过铸造它struct spa_log * log = iface; //使用工厂spa_log_warn的界面中的方法(日志,"您好世界!"); spa_log_info(日志,"版本:%i",log-> iface.version); //清除工厂spa_handle_clear(手柄)的句柄; }}}

获取第一个接口的名称(虽然我们可以通过spa_handle_factory_enum_interface_info循环,直到它返回0)和匹配的名称。

分配一个struct spa_handle *,这是一个指向所有界面的指针,工厂的方法,一种构造它的方法。 (我们必须取得它的大小,我相信将来可能会有一个更好的帮助者来实现这一目标。工厂创建可能采用参数。

使用spa_handle_get_Interface从创建的出厂获取我们的界面,然后将其投入到我们谈论的结构上,我们谈论了Astramier Struct Spa_log。

有很多插件和不同的方式使用它们。一些界面是异步和使用回调通过注册的事件handlersand钩子,有些是同步的。

这是一个概念SPA是什么。 它可能似乎繁琐且良好的速度下来了:固定在标题文件中定义的固定工厂名称,以及与默许加载库手动获取它们的常用方法,然后根据信息使用它的方法。 现在让我们看看Pod和Spa如何在Pipewire中使用。 PipeWire使用用于编码消息的POD(方法和事件) ......